home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 April / EnigmA AMIGA RUN 17 (1997)(G.R. Edizioni)(IT)[!][issue 1997-04][EAR-CD].iso / EARCD / comm / bbs / citsrc6K05.lha / msg.c < prev    next >
C/C++ Source or Header  |  1996-09-21  |  67KB  |  2,708 lines

  1. /*
  2. *                               msg.c
  3. *
  4. * Message handling for Citadel bulletin board system.
  5. */
  6. /*
  7. *                               history
  8. *
  9. * 86Aug15 HAW  Large chunk of History deleted due to space problems.
  10. * 84Mar29 HAW Start upgrade to BDS C 1.50a, identify _spr problem.
  11. * 83Mar03 CrT & SB   Various bug fixes...
  12. * 83Feb27 CrT  Save private mail for sender as well as recipient.
  13. * 83Feb23       Various.  transmitFile() won't drop first char on WC...
  14. * 82Dec06 CrT  2.00 release.
  15. * 82Nov05 CrT  Stream retrieval.  Handles messages longer than MAXTEXT.
  16. * 82Nov04 CrT  Revised disk format implemented.
  17. * 82Nov03 CrT  Individual history begun.  General cleanup.
  18. */
  19. #include "ctdl.h"
  20. /* #define TEST_SYS */
  21. /*
  22. *                               contents
  23. *
  24. *       AddNetMail()            manage adding mail to a net system
  25. *       aideMessage()           saves auto message in Aide>
  26. *       canRespond()            respond on the net checker
  27. *       CheckForwarding()       forward mail to another system?
  28. *       dGetWord()              reads a word off disk
  29. *       doActualWrite()         to allow two message files for bkp
  30. *       doFlush()               writes out to specified msg file
  31. *       DoRespond()             respond to mail.
  32. *       fakeFullCase()          converts uppercase message to mixed case
  33. *       flushMsgBuf()           wraps up message-to-disk store
  34. *       getRecipient()          get recipient for the message
  35. *       getWord()               gets one word from message buffer
  36. *       hldMessage()            handles held messages
  37. *       mAbort()                checks for user abort of typeout
  38. *       makeMessage()           menu-level message-entry routine
  39. *       mFormat()               formats a string to modem and console
  40. *       mPeek()                 sysop debugging tool--shows ctdlmsg.sys
  41. *       msgToDisk()             puts a message to the given disk file
  42. *       noteMessage()           enter message into current room
  43. *       noteAMessage()          noteMessage() local
  44. *       printMessage()          prints a message on modem & console
  45. *       pullIt()                sysop special message-removal routine
  46. *       putLong()               puts a long integer to file
  47. *       putMessage()            write message to disk
  48. *       putMsgChar()            writes successive message chars to disk
  49. *       putWord()               writes one word to modem & console
  50. *       replyMessage()          reply to a Mail> message
  51. *       showMessages()          menu-level show-roomful-of-messages fn
  52. *       ShowReply()             reply tracing function
  53. */
  54. extern char      netDebug;
  55. extern char      logNetResults;
  56. extern FILE         *msgfl;    /* file descriptor for the msg file      */
  57. extern FILE         *msgfl2;   /* disk based backup msg file            */
  58. AN_UNSIGNED crtColumn; /* current position on screen            */
  59. char pullMessage = FALSE;/* true to pull current message*/
  60. char        journalMessage = FALSE;
  61. SECTOR_ID   pulledMLoc;/* loc of pulled message         */
  62. MSG_NUMBER  pulledMId = 0l;    /* id of message to be pulled   */
  63. label       oldTarget;
  64. char        jrnlFile[100] = "";
  65. char        *NoNetRoomPrivs = "You do not have net privileges";
  66. char        outFlag = OUTOK;   /* will be one of the above     */
  67. char        heldMess;
  68. char        PrintBanner = FALSE;
  69. SListBase   SysList =
  70.   {
  71.   NULL, ChkCC, NULL, free, strdup
  72.  
  73.   };
  74. SListBase   FwdVortex =
  75.   {
  76.   NULL, ChkCC, NULL, free, NULL
  77.  
  78.   };
  79. char        EndWithCR = TRUE;
  80. int         AnonMsgCount;
  81. int         IckyCount, IckyLevel=1000;
  82. char        DisVandals;
  83. static char ReturnAddress[(2 * NAMESIZE) + 10];
  84. static char ArchiveMail;
  85. static char EOP = FALSE;
  86. char ReverseMessage;
  87. char MsgStreamEnter = FALSE;
  88. char Showing  = WHATEVER;
  89. char DiskHeld;
  90. int  ParanoiaLimit = 1000;
  91. int  ParanoiaCount;
  92. extern MessageBuffer   msgBuf;  /* The message buffer   */
  93. extern MessageBuffer   tempMess;        /* For held messages    */
  94. extern struct mBuf mFile1, mFile2;
  95. extern CONFIG    cfg;           /* Configuration variables      */
  96. extern logBuffer logBuf;        /* Buffer for the pippuls       */
  97. extern logBuffer logTmp;        /* Buffer for the pippuls       */
  98. extern aRoom     roomBuf;       /* Room buffer                  */
  99. extern rTable    *roomTab;
  100. extern NetBuffer netBuf, netTemp;
  101. extern FILE      *upfd;
  102. extern int       thisRoom;      /* Current room         */
  103. extern int       thisNet;       /* Current node in use    */
  104. extern int       thisLog;       /* Current log position  */
  105. extern int       outPut;
  106. extern NetTable  *netTab;
  107. extern char      *strFile;
  108. extern char      exChar;
  109. extern char      echo;  /* Output flag  */
  110. extern char      echoChar;
  111. extern char      loggedIn;       /* Logged in flag      */
  112. extern char      whichIO;        /* Who gets output?    */
  113. extern char      prevChar;       /* Output's evil purposes       */
  114. extern char      inNet;
  115. extern int       TransProtocol;  /* Flag        */
  116. extern char      haveCarrier;    /* Flag        */
  117. extern char      onConsole;      /* Flag        */
  118. extern char      remoteSysop;
  119. extern FILE      *netLog;
  120. /*
  121. * aideMessage()
  122. *
  123. * This function saves an auto message in Aide>.
  124. */
  125. void aideMessage(char *name, char noteDeletedMessage)
  126.   {
  127.   int ourRoom, target;
  128.   ourRoom = thisRoom;
  129.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(a) are:%d/%d\n",ourRoom,thisRoom);
  130.   /* message is already set up in msgBuf.mbtext */
  131.   putRoom(thisRoom);
  132.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(b) are:%d/%d\n",ourRoom,thisRoom);
  133.   if (name == NULL || (target = roomExists(name)) == ERROR)target = AIDEROOM;
  134.   getRoom(target);
  135.   strCpy(msgBuf.mbauth, "Citadel");
  136.   msgBuf.mbto[0]    = '\0';
  137.   msgBuf.mboname[0] = '\0';
  138.   putMessage(&logBuf);
  139.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(c) are:%d/%d\n",ourRoom,thisRoom);
  140.   if (noteDeletedMessage)
  141.     {
  142.     if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(d) are:%d/%d\n",ourRoom,thisRoom);
  143.     noteAMessage(roomBuf.msg, MSGSPERRM, pulledMId, pulledMLoc);
  144.     if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(e) are:%d/%d\n",ourRoom,thisRoom);
  145.  
  146.     }
  147.   putRoom(target);
  148.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(f) are:%d/%d\n",ourRoom,thisRoom);
  149.   noteRoom();
  150.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(g) are:%d/%d\n",ourRoom,thisRoom);
  151.   getRoom(ourRoom);
  152.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(h) are:%d/%d\n",ourRoom,thisRoom);
  153.  
  154.   }
  155. /*
  156. * canRespond()
  157. *
  158. * Can we set up an auto-response on the net?  This includes domain mail.
  159. */
  160. char canRespond()
  161.   {
  162.   int   cost, result;
  163.   char  dup;
  164.   label temp;
  165.   label domain;
  166.   if (inNet != NON_NET)
  167.   return FALSE;
  168.   ReturnAddress[0] = 0;
  169.   if (msgBuf.mborig[0] == 0 &&  /* i.e. is local mail      */
  170.   msgBuf.mboname[0] == 0)
  171.   return TRUE;
  172.   if (!logBuf.lbflags.NET_PRIVS)
  173.   return FALSE;
  174.   normId(msgBuf.mborig, temp);
  175.   domain[0] = 0;
  176.   if ((result = searchNet(temp, &netBuf)) == ERROR ||
  177.   (!netBuf.nbflags.local && !netBuf.nbflags.RouteLock))
  178.     {
  179.     if (msgBuf.mbdomain[0] != 0)
  180.       {
  181.       strCpy(domain, msgBuf.mbdomain);
  182.  
  183.       }
  184.     else if (!SystemInSecondary(msgBuf.mboname, domain, &dup) || dup)
  185.       {
  186.       if (result == ERROR) return FALSE;
  187.  
  188.       }
  189.  
  190.     }
  191.   if (strLen(domain) != 0)
  192.   sPrintf(ReturnAddress, "%s _ %s", msgBuf.mboname, domain);
  193.   else
  194.   strCpy(ReturnAddress, netBuf.netName);
  195.   if (domain[0] == 0 && netBuf.nbflags.local)
  196.   return TRUE;
  197.   if (domain[0] == 0)
  198.     {
  199.     if (msgBuf.mbdomain[0] != 0)
  200.     cost = FindCost(msgBuf.mbdomain);
  201.     else
  202.     cost = !netBuf.nbflags.local;
  203.  
  204.     }
  205.   else cost = FindCost(domain);
  206.   if (logBuf.credit >= cost)
  207.   return TRUE;
  208.   /* Sysop always has enough credits kludge */
  209.   if (HalfSysop())
  210.     {
  211.     logBuf.credit += cost;
  212.     return TRUE;
  213.  
  214.     }
  215.   mPrintf("\n Not enough LD credit.\n ");
  216.   return FALSE;
  217.  
  218.   }
  219. /*
  220. * deleteMessage()
  221. *
  222. * This function deletes a message for pullIt().
  223. */
  224. char deleteMessage(int m)
  225.   {
  226.   char auth[129];
  227.   /* record vital statistics for possible insertion elsewhere: */
  228.   DelMsg(TRUE, m);
  229.   strCpy(auth, msgBuf.mbauth);
  230.   ZeroMsgBuffer(&msgBuf);
  231.   /* note in Aide>: */
  232.   sPrintf(msgBuf.mbtext, "Following message from %s deleted by %s:",
  233.   (auth[0]) ? auth : "<anonymous>", logBuf.lbname);
  234.   aideMessage(NULL, /* noteDeletedMessage== */ TRUE);
  235.   return TRUE;
  236.  
  237.   }
  238. /*
  239. * DelMsg()
  240. *
  241. * This does actual work of deleting a msg from current room.
  242. */
  243. void DelMsg(char killit, int m)
  244.   {
  245.   int SaveRoom;
  246.   int i;
  247.   pulledMLoc = roomBuf.msg[m].rbmsgLoc;
  248.   #ifdef NORMAL_MESSAGES
  249.   pulledMId  = roomBuf.msg[m].rbmsgNo ;
  250.   #else
  251.   pulledMId  = (roomBuf.msg[m].rbmsgNo & S_MSG_MASK);
  252.   #endif
  253.   if (thisRoom == AIDEROOM || !killit)   return ;
  254.   /* return emptied slot: */
  255.   for (i = m;  i > 0;  i--)
  256.     {
  257.     roomBuf.msg[i].rbmsgLoc      = roomBuf.msg[i - 1].rbmsgLoc;
  258.     roomBuf.msg[i].rbmsgNo       = roomBuf.msg[i - 1].rbmsgNo ;
  259.  
  260.     }
  261.   roomBuf.msg[0].rbmsgNo   = 0l;         /* mark new slot at end as free */
  262.   roomBuf.msg[0].rbmsgLoc  = 0;  /* mark new slot at end as free */
  263.   /* store revised room to disk before we forget...   */
  264.   SaveRoom = thisRoom;
  265.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(a) are:%d/%d\n",SaveRoom,thisRoom);
  266.   noteRoom();
  267.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(b) are:%d/%d\n",SaveRoom,thisRoom);
  268.   putRoom(thisRoom);
  269.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(c) are:%d/%d\n",SaveRoom,thisRoom);
  270.   getRoom(SaveRoom);
  271.   if (cfg.BoolFlags.debug) splitF(netLog, "Current Rooms(d) are:%d/%d\n",SaveRoom,thisRoom);
  272.  
  273.   }
  274. /*
  275. * dGetWord()
  276. *
  277. * This function fetches one word from current message, off disk.  It returns
  278. * TRUE if more words follow, else FALSE.
  279. */
  280. char dGetWord(char *dest, int lim)
  281.   {
  282.   int  c;
  283.   --lim;         /* play it safe */
  284.   /* pick up any leading blanks: */
  285.   for (c = getMsgChar(); (c == '\n' || c == ' ')  &&  c && lim;
  286.   c = getMsgChar())
  287.     {
  288.     if (lim)
  289.       {
  290.       *dest++ = c;   lim--;
  291.  
  292.       }
  293.  
  294.     }
  295.   /* step through word: */
  296.   for (; c != '\n' && c != ' ' && c && lim;   c = getMsgChar())
  297.     {
  298.     if (lim)
  299.       {
  300.       *dest++ = c;   lim--;
  301.  
  302.       }
  303.  
  304.     }
  305.   if (*(dest - 1) != '\n')
  306.   /* trailing blanks: */
  307.   for (;   c == ' ' && c && lim;   c = getMsgChar())
  308.     {
  309.     if (lim)
  310.       {
  311.       *dest++ = c;   lim--;
  312.  
  313.       }
  314.  
  315.     }
  316.   if (c)  unGetMsgChar(c);    /* took one too many    */
  317.   *dest = '\0';         /* tie off string        */
  318.   return (char) c;
  319.  
  320.   }
  321. /*
  322. * doActualWrite()
  323. *
  324. * This is used to help mirror a message on both disk and in RAM disk (two
  325. * message files).
  326. */
  327. char doActualWrite(FILE *whichmsg, struct mBuf *mFile, char c)
  328.   {
  329.   MSG_NUMBER  temp;
  330.   int           toReturn = 0;
  331.   if (mFile->sectBuf[mFile->thisChar] == 0xFF)
  332.     {
  333.     /* obliterating a msg   */
  334.     toReturn = 1;
  335.  
  336.     }
  337.   mFile->sectBuf[mFile->thisChar]   = c;
  338.   mFile->thisChar    = ++mFile->thisChar % MSG_SECT_SIZE;
  339.   if (mFile->thisChar == 0)
  340.     {
  341.     /* time to write sector out and get next: */
  342.     temp = mFile->thisSector;
  343.     temp *= MSG_SECT_SIZE;
  344.     fseek(whichmsg, temp, 0);
  345.     crypte(mFile->sectBuf, MSG_SECT_SIZE, 0);
  346.     if (fwrite(mFile->sectBuf, MSG_SECT_SIZE, 1, whichmsg) != 1)
  347.       {
  348.       crashout("?putMsgChar-write fail");
  349.  
  350.       }
  351.     mFile->thisSector = ++mFile->thisSector % cfg.maxMSector;
  352.     temp = mFile->thisSector;
  353.     temp *= MSG_SECT_SIZE;
  354.     fseek(whichmsg, temp, 0);
  355.     if (fread(mFile->sectBuf, MSG_SECT_SIZE, 1, whichmsg) != 1)
  356.       {
  357.       crashout("?putMsgChar-read fail");
  358.  
  359.       }
  360.     crypte(mFile->sectBuf, MSG_SECT_SIZE, 0);
  361.  
  362.     }
  363.   return  (char)toReturn;
  364.  
  365.   }
  366. /*
  367. * doFlush()
  368. *
  369. * This does actual write for specified msg file.
  370. */
  371. void doFlush(FILE *whichmsg, struct mBuf *mFile)
  372.   {
  373.   long int s;
  374.   s = mFile->thisSector;
  375.   s *= MSG_SECT_SIZE;
  376.   fseek(whichmsg, s, 0);
  377.   crypte(mFile->sectBuf, MSG_SECT_SIZE, 0);
  378.   if (fwrite(mFile->sectBuf, MSG_SECT_SIZE, 1, whichmsg) != 1)
  379.     {
  380.     crashout("?ctdlmsg.sys write fail");
  381.  
  382.     }
  383.   crypte(mFile->sectBuf, MSG_SECT_SIZE, 0);
  384.   fflush(whichmsg);
  385.  
  386.   }
  387. /*
  388. * DoRespond()
  389. *
  390. * Does the user want to respond to mail or even skip the mail?
  391. */
  392. int DoRespond()
  393.   {
  394.   int toReturn = -2;
  395.   for (doCR(); onLine() && toReturn == -2; )
  396.     {
  397.     outFlag = IMPERVIOUS;
  398.     mPrintf("respond? (Y/N/Skip): ");
  399.     switch (toUpper(iChar()))
  400.       {
  401.       case 'Y': toReturn = TRUE; break;
  402.       case 'N': toReturn = FALSE; break;
  403.       case 'S': mPrintf("kip message"); toReturn = ERROR; break;
  404.  
  405.       }
  406.     doCR();
  407.  
  408.     }
  409.   outFlag = OUTOK;
  410.   return toReturn;
  411.  
  412.   }
  413. /*
  414. * fakeFullCase()
  415. *
  416. * This function converts a message in uppercase-only to a reasonable mix.  It
  417. * can't possibly make matters worse...
  418. *
  419. * Algorithm: First alphabetic after a period is uppercase, all others are
  420. * lowercase, excepting pronoun "I" is a special case.  We assume an imaginary
  421. * period preceding the text.
  422. */
  423. void fakeFullCase(char *text)
  424.   {
  425.   char *c;
  426.   char lastWasPeriod;
  427.   char state;
  428.   for(lastWasPeriod=TRUE, c=text;   *c;  c++)
  429.     {
  430.     if (
  431.     *c != '.'
  432.     &&
  433.     *c != '?'
  434.     &&
  435.     *c != '!'
  436.     )
  437.       {
  438.       if (isAlpha(*c))
  439.         {
  440.         if (lastWasPeriod)      *c       = toUpper(*c);
  441.         else if (*c != 'l')     *c       = toLower(*c);
  442.         lastWasPeriod   = FALSE;
  443.  
  444.         }
  445.  
  446.       }
  447.     else
  448.       {
  449.       lastWasPeriod      = TRUE ;
  450.  
  451.       }
  452.  
  453.     }
  454.   /* little state machine to search for ' i ': */
  455.   #define NUTHIN          0
  456.   #define FIRSTBLANK     1
  457.   #define BLANKI          2
  458.   for (state=NUTHIN, c=text;  *c;  c++)
  459.     {
  460.     switch (state)
  461.       {
  462.       case NUTHIN:
  463.       if (isSpace(*c))    state   = FIRSTBLANK;
  464.       else              state   = NUTHIN    ;
  465.       break;
  466.       case FIRSTBLANK:
  467.       if (*c == 'i')     state   = BLANKI    ;
  468.       else              state   = NUTHIN    ;
  469.       break;
  470.       case BLANKI:
  471.       if (isSpace(*c))    state   = FIRSTBLANK;
  472.       else              state   = NUTHIN    ;
  473.       if (!isAlpha(*c))   *(c-1)  = 'I';
  474.       break;
  475.  
  476.       }
  477.  
  478.     }
  479.  
  480.   }
  481. /*
  482. * flushMsgBuf()
  483. *
  484. * This function wraps up writing a message to disk, takes into account 2nd
  485. * msg file if necessary.
  486. */
  487. void flushMsgBuf()
  488.   {
  489.   doFlush(msgfl, &mFile1);
  490.   if (cfg.BoolFlags.mirror)
  491.   doFlush(msgfl2, &mFile2);
  492.  
  493.   }
  494. /*
  495. * getWord()
  496. *
  497. * This function fetches one word from current message.
  498. */
  499. int getWord(char *dest, char *source, int offset, int lim)
  500.   {
  501.   int i, j;
  502.   /* skip leading blanks if any */
  503.   for (i = 0; (source[offset+i] == '\n' || source[offset+i] == ' ') &&
  504.   i < lim - 1;  i++);
  505.   /* step over word */
  506.   for (;
  507.   source[offset+i]   != ' '     &&
  508.   source[offset+i]   != '\n'     &&
  509.   i               <  lim - 1 &&
  510.   source[offset+i]   != 0;
  511.   i++
  512.   );
  513.   if (source[offset + i - 1] != '\n')
  514.   /* pick up any trailing blanks */
  515.   for (;  source[offset+i]==' ' && i<lim - 1;  i++);
  516.   /* copy word over */
  517.   for (j = 0; j < i; j++)  dest[j] = source[offset+j];
  518.   dest[j] = 0;  /* null to tie off string */
  519.   return(offset+i);
  520.  
  521.   }
  522. /*
  523. * mAbort()
  524. *
  525. * This function returns TRUE if the user has aborted typeout.
  526. *
  527. * Globals modified:      outFlag
  528. */
  529. char mAbort()
  530.   {
  531.   extern SListBase Moderators;
  532.   char c, toReturn, oldEcho;
  533.   /* Check for abort/pause from user */
  534.   if (outFlag == IMPERVIOUS || outFlag == NET_CALL || outPut == DISK)
  535.     {
  536.     toReturn    = FALSE;
  537.  
  538.     }
  539.   else if (!BBSCharReady())
  540.     {
  541.     if (haveCarrier && !gotCarrier())
  542.       {
  543.       modIn();      /* Let modIn() report the problem    */
  544.       toReturn  = TRUE;
  545.  
  546.       }
  547.     else if (!onConsole && KBReady() && !PrintBanner)
  548.       {
  549.       if (!SurreptitiousChar(getCh()))
  550.         {
  551.         outFlag     = OUTSKIP;
  552.         toReturn    = TRUE;
  553.  
  554.         }
  555.  
  556.       }
  557.     else toReturn       = FALSE;
  558.  
  559.     }
  560.   else
  561.     {
  562.     oldEcho  = echo;
  563.     echo     = NEITHER;
  564.     echoChar = 0;
  565.     c = toUpper(modIn());   /* avoid the filter */
  566.     switch (c)
  567.       {
  568.       case XOFF:
  569.       while (iChar() != XON && (gotCarrier() || onConsole))
  570.       ;
  571.       toReturn = FALSE;
  572.       break;
  573.       case ' ':                             /* Allow space to pause*/
  574.       case 'P':                             /*  pause:   */
  575.       c = iChar();                          /* wait to resume */
  576.       toReturn   = FALSE;
  577.       if (     toUpper(c) == 'D' &&
  578.       Showing == MSGS &&
  579.       (aide ||
  580.       (strCmpU(logBuf.lbname, AskForNSMap(&Moderators,
  581.       thisRoom)) == SAMESTRING
  582.       && strLen(logBuf.lbname) != 0)))
  583.       pullMessage = TRUE;
  584.       else if (toUpper(c) == 'J' && HalfSysop())
  585.       journalMessage = TRUE;
  586.       /*
  587.       * We do things this way to avoid getting caught in a
  588.       * recursive trap which I don't really care to trace down
  589.       * at the moment involving mPrintf accidentally "calling"
  590.       * message entry routines when it really shouldn't.  Not
  591.       * only can that get messy, we could also run into some stack
  592.       * overflow problems.  So instead we'll use this "kludge"
  593.       * which may actually yield a better behavior for us -- it
  594.       * lets us reprint the interrupted message after message
  595.       * composition.  Note we allow this option in Mail but it acts
  596.       * entirely differently -- see showMessage().  Also note we track
  597.       * READ-ONLY rooms here, too, rather than in showMessage().
  598.       */
  599.       else if (toUpper(c) == 'E' && Showing == MSGS && HasWritePrivs())
  600.         {
  601.         MsgStreamEnter = TRUE;
  602.         outFlag     = OUTSKIP;
  603.         toReturn = TRUE;
  604.  
  605.         }
  606.       break;
  607.       case 'J':                            /* jump paragraph:*/
  608.       outFlag     = OUTPARAGRAPH;
  609.       toReturn    = FALSE;
  610.       break;
  611.       case 'N':                            /* next:       */
  612.       outFlag     = OUTNEXT;
  613.       toReturn    = TRUE;
  614.       break;
  615.       case 'S':                            /* skip:       */
  616.       outFlag     = OUTSKIP;
  617.       toReturn    = TRUE;
  618.       break;
  619.       case 'R':
  620.       if (Showing == MSGS)
  621.         {
  622.         pause(50);
  623.         ReverseMessage = TRUE;
  624.  
  625.         }
  626.       toReturn    = FALSE;      /* so we don't stop output */
  627.       break;
  628.       case 7: /* anytime net indicator */
  629.       case 68: /* stroll indicator */
  630.       if (PrintBanner &&
  631.       ((c == 7 && cfg.BoolFlags.netParticipant) ||
  632.       c == 68))
  633.         {
  634.         if (c == 7)
  635.           {
  636.           if (CheckForSpecial(13, 69))
  637.             {
  638.             outFlag  = NET_CALL;
  639.             toReturn = TRUE;
  640.  
  641.             }
  642.  
  643.           }
  644.         else
  645.           {
  646.           if (CheckForSpecial(79, 35))
  647.             {
  648.             outFlag  = STROLL_DETECTED;
  649.             toReturn = TRUE;
  650.  
  651.             }
  652.  
  653.           }
  654.         break;
  655.  
  656.         }
  657.       default:
  658.       toReturn    = FALSE;
  659.       break;
  660.  
  661.       }
  662.     echo    = oldEcho;
  663.  
  664.     }
  665.   return toReturn;
  666.  
  667.   }
  668. /*
  669. * CheckForSpecial()
  670. *
  671. * This checks to see if a special call is incoming.  This can be a network
  672. * call or a stroll call (not implemented, really).
  673. */
  674. char CheckForSpecial(int second, int third)
  675.   {
  676.   if (receive(1) == second)
  677.   if (receive(1) == third)
  678.   return TRUE;
  679.   return FALSE;
  680.  
  681.   }
  682. /*
  683. * getRecipient()
  684. *
  685. * This gets the recipient for the message (Mail> only, of course).
  686. */
  687. char getRecipient()
  688.   {
  689.   label person;
  690.   if (thisRoom != MAILROOM)
  691.     {
  692.     msgBuf.mbto[0] = 0;      /* Zero recipient   */
  693.     return TRUE;
  694.  
  695.     }
  696.   if (msgBuf.mbto[0] == 0)
  697.     {
  698.     if (!loggedIn || (!aide && cfg.BoolFlags.noMail) ||
  699.     logBuf.lbflags.TWIT)
  700.       {
  701.       strCpy(msgBuf.mbto, "Sysop");
  702.       mPrintf(" (private mail to 'sysop')\n ");
  703.       return TRUE;
  704.  
  705.       }
  706.     getNormStr("RECIPM", msgBuf.mbto, sizeof msgBuf.mbto, 0);
  707.     if (strLen(msgBuf.mbto) == 0) return FALSE;
  708.  
  709.     }
  710.   switch (SepNameSystem(msgBuf.mbto, person, msgBuf.mbaddr, &netBuf))
  711.     {
  712.     case IS_SYSTEM:
  713.     if (!NetValidate(TRUE) || !netInfo(FALSE)) return FALSE;
  714.     strCpy(msgBuf.mbto, person);
  715.     /* strCpy(msgBuf.mbaddr, netBuf.netName); */
  716.     case NOT_SYSTEM:
  717.     break;
  718.     default:
  719.     mPrintf("Couldn't find target system.\n ");
  720.     case SYSTEM_IS_US:
  721.     return FALSE;
  722.  
  723.     }
  724.   if (!msgBuf.mbaddr[0])
  725.     {
  726.     if (strCmpU(msgBuf.mbto, logBuf.lbname) == SAMESTRING)
  727.       {
  728.       Output_Citadel_Message("NOSELF",NULL, NULL, NULL);
  729.       return FALSE;
  730.  
  731.       }
  732.     if (PersonExists(msgBuf.mbto) == ERROR)
  733.       {
  734.       Output_Citadel_Message("NOPERS",(long)msgBuf.mbto,NULL, NULL);
  735.       msgBuf.mbto[0] = 0;            /* Zero recipient   */
  736.       return FALSE;
  737.  
  738.       }
  739.  
  740.     }
  741.   return TRUE;
  742.  
  743.   }
  744. /*
  745. * replyMessage()
  746. *
  747. * This function will get a reply to a Mail> message.
  748. */
  749. char replyMessage(MSG_NUMBER msgNo, SECTOR_ID Loc)
  750.   {
  751.   label who;
  752.   char  other[O_NET_PATH_SIZE];
  753.   if (heldMess)
  754.     {
  755.     if (strCmpU(msgBuf.mbauth, tempMess.mbto) == SAMESTRING)
  756.       {
  757.       return hldMessage(TRUE);
  758.  
  759.       }
  760.  
  761.     }
  762.   strncpy(who, msgBuf.mbauth,sizeof(who)-1);
  763.   strCpy(other, msgBuf.mbOther);
  764.   ZeroMsgBuffer(&msgBuf);
  765.   strCpy(msgBuf.mbto, who);
  766.   strCpy(msgBuf.mbaddr, ReturnAddress);
  767.   sPrintf(msgBuf.mbreply, "%u:%lu", Loc, msgNo);        /* back ptr */
  768.   if (strLen(other) != 0)
  769.     {
  770.     mPrintf("%s address is '%s'.", netBuf.netName, other);
  771.     if (!getYesNo("OKPROM"))
  772.       {
  773.       mPrintf("%s address", netBuf.netName);
  774.       getNormStr("", msgBuf.mbOther, O_NET_PATH_SIZE, 0);
  775.  
  776.       }
  777.     else
  778.     strCpy(msgBuf.mbOther, other);
  779.  
  780.     }
  781.   return (char)(procMessage(ASCII, TRUE) == TRUE);
  782.  
  783.   }
  784. /*
  785. * hldMessage()
  786. *
  787. * This function handles held messages
  788. * TRUE indicates a message was added to the room
  789. * FALSE indicates room not disturbed (either msg was re-held or aborted)
  790. */
  791. char hldMessage(char IsReply)
  792.   {
  793.   int result;
  794.   if (!heldMess)
  795.     {
  796.     mPrintf(" \n No message in the Hold buffer!\007\n ");
  797.     return FALSE;
  798.  
  799.     }
  800.   heldMess = FALSE;
  801.   MoveMsgBuffer(&msgBuf, &tempMess);
  802.   ZeroMsgBuffer(&tempMess);
  803.   if (DiskHeld)
  804.     {
  805.     DiskHeld = FALSE;
  806.     if ((result = roomExists(msgBuf.mbroom)) != ERROR)
  807.       {
  808.       if (KnownRoom(result))
  809.         {
  810.         mPrintf("\n Moving to %s.\n ", msgBuf.mbroom);
  811.         getRoom(result);
  812.         setUp(FALSE);
  813.         if (thisRoom == MAILROOM)
  814.         echo = CALLER;
  815.  
  816.         }
  817.  
  818.       }
  819.  
  820.     }
  821.   if (roomTab[thisRoom].rtflags.SHARED == 0)
  822.   msgBuf.mboname[0] = 0;
  823.   else if (loggedIn && roomBuf.rbflags.SHARED &&
  824.   roomBuf.rbflags.AUTO_NET &&
  825.   (roomBuf.rbflags.ALL_NET || logBuf.lbflags.NET_PRIVS))
  826.   netInfo(TRUE);
  827.   /*
  828.   * this indicates the user did a .eh in Mail> and
  829.   * failed to type in a valid user name.  This saves the
  830.   * message back to the held buffer, rather than losing
  831.   * it (a nettlesome behavior).
  832.   */
  833.   if ((result = procMessage(ASCII, IsReply)) == ERROR)
  834.     {
  835.     MoveMsgBuffer(&tempMess, &msgBuf);
  836.     heldMess = TRUE;
  837.     return FALSE;               /* indicate what happened */
  838.  
  839.     }
  840.   return (char)result;
  841.  
  842.   }
  843. /*
  844. * makeMessage()
  845. *
  846. * This is a menu-level routine to enter a message.
  847. *
  848. * Return: TRUE if message saved else FALSE.
  849. */
  850. int makeMessage(char uploading)
  851.   {
  852.   if (!loggedIn && AnonMsgCount > 1)
  853.   return FALSE;
  854.   if (loggedIn && roomBuf.rbflags.SHARED &&
  855.   roomBuf.rbflags.AUTO_NET &&
  856.   (roomBuf.rbflags.ALL_NET || logBuf.lbflags.NET_PRIVS))
  857.   return netMessage(uploading);
  858.   ZeroMsgBuffer(&msgBuf);
  859.   return procMessage(uploading, FALSE);
  860.  
  861.   }
  862. /*
  863. * idiotMessage()
  864. *
  865. * This checks for idiocy by the user.
  866. */
  867. char idiotMessage()
  868.   {
  869.   int base, rover;
  870.   if (DisVandals || loggedIn || thisRoom != MAILROOM)
  871.   return FALSE;
  872.   for (base = 0; msgBuf.mbtext[base]; base++)
  873.     {
  874.     for (rover = 0; rover < IDIOT_TRIGGER; rover++)
  875.       {
  876.       if (msgBuf.mbtext[base] != msgBuf.mbtext[base + rover] ||
  877.       msgBuf.mbtext[base] == ' ')
  878.       break;
  879.  
  880.       }
  881.     if (rover == IDIOT_TRIGGER) return TRUE;    /* Jackass caught! */
  882.  
  883.     }
  884.   if (cfg.AnonMailLength > 0 && strLen(msgBuf.mbtext) > cfg.AnonMailLength &&
  885.   !onConsole)
  886.     {
  887.     HangUp(TRUE);
  888.     sPrintf(tempMess.mbtext, "   %s @ %s\n%s\n", formDate(), Current_Time(),
  889.     msgBuf.mbtext);
  890.     CallMsg("anonmail", tempMess.mbtext);
  891.     strCpy(msgBuf.mbtext, "Indecently long anonymous Mail has been stored in ANONMAIL.");
  892.     aideMessage(NULL,FALSE);
  893.     return TRUE;
  894.  
  895.     }
  896.   return FALSE;
  897.  
  898.   }
  899. /*
  900. * procMessage
  901. *
  902. * This is a menu-level routine to enter a message.
  903. */
  904. int procMessage(char uploading, char IsReply)
  905.   {
  906.   char   *pc, allUpper;
  907.   extern SListBase BadWords;
  908.   extern char BadMessages[];
  909.   extern char MsgEntryType;
  910.   if (!HasWritePrivs())
  911.     {
  912.     mPrintf("This is a Read Only room.");
  913.     return FALSE;
  914.  
  915.     }
  916.   if (loggedIn)
  917.   strCpy(msgBuf.mbauth, (roomBuf.rbflags.ANON) ? "" : logBuf.lbname);
  918.   strCpy(msgBuf.mbroom, roomBuf.rbname);
  919.   strCpy(msgBuf.mbdate, (roomBuf.rbflags.ANON) ? "****" :  formDate());
  920.   if (!getRecipient())
  921.     {
  922.     return ERROR;
  923.  
  924.     }
  925.   MsgEntryType = MSG_ENTRY;
  926.   if (getText(uploading) == TRUE)
  927.     {
  928.     if (!getRecipient())
  929.       {
  930.       mPrintf("Unexpected internal error, please report it!\n ");
  931.       return FALSE;
  932.  
  933.       }
  934.     /* Asshole check */
  935.     if (idiotMessage())
  936.       {
  937.       strCpy(msgBuf.mbtext, "Vandalism attempt.");
  938.       aideMessage(NULL,FALSE);
  939.       return FALSE;
  940.  
  941.       }
  942.     if ( (!loggedIn || thisRoom != MAILROOM)
  943.       &&  SearchList(&BadWords, msgBuf.mbtext) != NULL )
  944.       {
  945.       if (++IckyCount > IckyLevel && !aide)
  946.         {
  947.         logMessage(BADWORDS_SIGNAL, "", 'E');
  948.         if (!onConsole) HangUp(TRUE);
  949.  
  950.         }
  951.       if (!aide)
  952.         {
  953.         if (strLen(BadMessages) != 0)
  954.           {
  955.           if (redirect(BadMessages))
  956.             {
  957.             printMessage(1);
  958.             undirect();
  959.  
  960.             }
  961.  
  962.           }
  963.         return FALSE;
  964.  
  965.         }
  966.  
  967.       }
  968.     for (pc=msgBuf.mbtext, allUpper=TRUE;   *pc && allUpper;  pc++)
  969.       {
  970.       if (toUpper(*pc) != *pc && *pc != 'l')   allUpper = FALSE;
  971.  
  972.       }
  973.     if (allUpper)   fakeFullCase(msgBuf.mbtext);
  974.     if (!logBuf.lbflags.TWIT)
  975.     putMessage(&logBuf);
  976.     AnonMsgCount++;
  977.     if (!IsReply && ++ParanoiaCount > ParanoiaLimit && !aide)
  978.       {
  979.       logMessage(BADWORDS_SIGNAL, "", 'E');
  980.       if (!onConsole) HangUp(TRUE);
  981.  
  982.       }
  983.     return TRUE;
  984.  
  985.     }
  986.   return FALSE;
  987.  
  988.   }
  989. /*
  990. * HasWritePrivs()
  991. *
  992. * This checks to see if the write privileges are yes.
  993. */
  994. char HasWritePrivs()
  995.   {
  996.   extern SListBase Moderators;
  997.   if (roomBuf.rbflags.READ_ONLY && !aide &&
  998.   knowRoom(&logBuf, thisRoom) != WRITE_PRIVS &&
  999.   !(strCmpU(logBuf.lbname, AskForNSMap(&Moderators,
  1000.   thisRoom)) == SAMESTRING &&
  1001.   strLen(logBuf.lbname) != 0))
  1002.   return FALSE;
  1003.   else
  1004.   return TRUE;
  1005.  
  1006.   }
  1007. /*
  1008. * EatIcky()
  1009. *
  1010. * This function will eat an icky word for the MakeList handler.
  1011. */
  1012. void *EatIcky(char *str)
  1013.   {
  1014.   if (strLen(str) != 0) return strdup(str);
  1015.   return NULL;
  1016.  
  1017.   }
  1018. /*
  1019. * FindIcky()
  1020. *
  1021. * This will check to see if a word exists in the current message.  This is
  1022. * used by the list handling functions.
  1023. */
  1024. void *FindIcky(char *word, char *str)
  1025.   {
  1026.   char *c;
  1027.   if ((c = matchString(str, word, lbyte(str) - 1)) != NULL)
  1028.   splitF(netLog, "FindIcky found word -%s- in message.\n", word);
  1029.   return c;
  1030.  
  1031.   }
  1032. /**
  1033. * EatIckyPeople()
  1034. *
  1035. * This function will eat an icky person for the MakeList handler.
  1036. * See definition of User_Id for more info.
  1037. **/
  1038. void *EatIckyPeople(char *str)
  1039.   {
  1040.   char *item;
  1041.   struct User_Id *ptr;
  1042.   if (strLen(str) != 0)
  1043.     {
  1044.     ptr =  GetDynamic( sizeof( struct User_Id) );
  1045.     item = strtok(str,",");           /* name of person to suppress */
  1046.     if( item != NULL )
  1047.       {
  1048.       ptr->name = strdup(item);
  1049.       item      = strtok(NULL,",");   /* name of room to suppress */
  1050.       if( item != NULL )
  1051.         {
  1052.         ptr->room = strdup(item);
  1053.         item      = strtok(NULL,","); /* name of node to suppress */
  1054.         if( item != NULL )
  1055.           {
  1056.           ptr->system = strdup(item);
  1057.           if( strCmpU(ptr->room,  "*" ) == SAMESTRING &&
  1058.               strCmpU(ptr->name,  "*" ) == SAMESTRING &&
  1059.               strCmpU(ptr->system,"*" ) == SAMESTRING )
  1060.             {
  1061.             splitF(netLog, "Invalid room/name/system, cannot be all *");
  1062.             }
  1063.           else return (void *)ptr;
  1064.           }
  1065.         else splitF(netLog, "Invalid NodeName to EatIckyPeople:%s.\n",str);
  1066.         }
  1067.       else   splitF(netLog, "Invalid Room to EatIckyPeople:%s.\n", str);
  1068.        }
  1069.     else     splitF(netLog, "Invalid Name to EatIckyPeople:%s.\n", str);
  1070.     };
  1071.   return NULL;
  1072.  
  1073.   }
  1074. /*
  1075. * FindIckyPeople()
  1076. *
  1077. * This will check to see if a person/Room/System exists in the current message.
  1078. * This is used by the list handling functions.
  1079. */
  1080. void *FindIckyPeople( struct User_Id *User_Record,  MessageBuffer *msg)
  1081.   {
  1082.   char *c;
  1083.   c = NULL;
  1084.   if( strCmpU(User_Record->name, msg->mbauth) == SAMESTRING
  1085.    || strCmpU(User_Record->name,         "*") == SAMESTRING )
  1086.     {
  1087.     if( strCmpU(User_Record->room, msg->mbroom) == SAMESTRING
  1088.      || strCmpU(User_Record->room,         "*") == SAMESTRING )
  1089.       {
  1090.       if( strCmpU(User_Record->system, msg->mboname) == SAMESTRING
  1091.        || strCmpU(User_Record->system,          "*") == SAMESTRING )
  1092.         {
  1093.         c = User_Record->name;
  1094.         splitF(netLog, "FindIckyPeople found -%s/%s/%s - in message.\n"
  1095.       , User_Record->name, User_Record->room, User_Record->system);
  1096.         };
  1097.       };
  1098.     };
  1099.   return c;
  1100.  
  1101.   }
  1102. /*
  1103. * mFormat()
  1104. *
  1105. * This function does the work of formatting a string to modem and console.
  1106. */
  1107. void mFormat(char *string)
  1108.   {
  1109.   char wordBuf[MAXWORD];
  1110.   int  i;
  1111.   for (i = 0;  string[i] && (outFlag == OUTOK    ||
  1112.   outFlag == IMPERVIOUS ||
  1113.   outFlag == OUTPARAGRAPH);  )
  1114.     {
  1115.     i = getWord(wordBuf, string, i, MAXWORD);
  1116.     putWord(wordBuf);
  1117.     if (mAbort()) return;
  1118.  
  1119.     }
  1120.  
  1121.   }
  1122. /*
  1123. * moveMessage()
  1124. *
  1125. * This function moves a message for pullIt().
  1126. */
  1127. char moveMessage(char which, int m, char *toReturn)
  1128.   {
  1129.   label blah;
  1130.   int   roomTarg, ourRoom;
  1131.   int   curRoom;
  1132.   char  tempauth[129];
  1133.   curRoom = thisRoom;
  1134.   if (!getXString("DESTRM", blah, 20, oldTarget, oldTarget))
  1135.   return FALSE;
  1136.   if ((roomTarg = roomCheck(roomExists, blah)) == ERROR)
  1137.     {
  1138.     if ((roomTarg = roomCheck(partialExist, blah)) == ERROR)
  1139.       {
  1140.       mPrintf("'%s' does not exist.", blah);
  1141.       return FALSE;
  1142.  
  1143.       }
  1144.     else
  1145.       {
  1146.       thisRoom = roomTarg;
  1147.       if (roomCheck(partialExist, blah) != ERROR)
  1148.         {
  1149.         thisRoom = curRoom;
  1150.         mPrintf("'%s' is not a unique string.", blah);
  1151.         return FALSE;
  1152.  
  1153.         }
  1154.       thisRoom = curRoom;
  1155.  
  1156.       }
  1157.  
  1158.     }
  1159.   strCpy(oldTarget, roomTab[roomTarg].rtname);
  1160.   ourRoom = thisRoom;
  1161.   DelMsg(which == 'M', m);
  1162.   getRoom(roomTarg);
  1163.   noteAMessage(roomBuf.msg, MSGSPERRM, pulledMId, pulledMLoc);
  1164.   putRoom(thisRoom);
  1165.   noteRoom();
  1166.   /* is message is going to a shared room ... */
  1167.   if (roomBuf.rbflags.SHARED)
  1168.     {
  1169.     /* if message was originally netted or if aide wants message shared */
  1170.     if (strLen(msgBuf.mboname) != 0 || getYesNo("MKENET"))
  1171.     MakeNetted(MSGSPERRM - 1);
  1172.  
  1173.     }
  1174.   getRoom(ourRoom);
  1175.   strCpy(tempauth, msgBuf.mbauth);
  1176.   ZeroMsgBuffer(&msgBuf);
  1177.   sPrintf(
  1178.   msgBuf.mbtext,
  1179.   "Following message from %s %sed from %s",
  1180.   (tempauth[0]) ? tempauth : "<anonymous>",
  1181.   (which == 'M') ? "mov" : "copi",
  1182.   formRoom(thisRoom, FALSE, FALSE));
  1183.   sPrintf(lbyte(msgBuf.mbtext), " to %s by %s",
  1184.   formRoom(roomTarg, FALSE, FALSE),
  1185.   logBuf.lbname
  1186.   );
  1187.   aideMessage(NULL, /* noteDeletedMessage == */ TRUE);
  1188.   *toReturn = (which == 'M') ? DELETED : NO_CHANGE;
  1189.   return TRUE;
  1190.  
  1191.   }
  1192. /*
  1193. * mPeek()
  1194. *
  1195. * This function dumps a sector in msgBuf.  sysop debugging tool.
  1196. */
  1197. void mPeek()
  1198.   {
  1199.   #ifdef TEST_SYS
  1200.   char visible();
  1201.   char blup[50];
  1202.   DATA_BLOCK peekBuf;
  1203.   int  col, row;
  1204.   MSG_NUMBER r, s;
  1205.   s = getNumber("DUMPSC", 0l, (MSG_NUMBER) (cfg.maxMSector-1));
  1206.   r = s * MSG_SECT_SIZE;
  1207.   fseek(msgfl, r, 0);
  1208.   fread(peekBuf, MSG_SECT_SIZE, 1, msgfl);
  1209.   crypte(peekBuf, MSG_SECT_SIZE, 0);
  1210.   for (row = 0;  row < 2;  row++)
  1211.     {
  1212.     mPrintf("\n ");
  1213.     for (col = 0;  col < 64;  col++)
  1214.       {
  1215.       mPrintf("%c", visible(peekBuf[row*64 +col]));
  1216.       if (!isprint(peekBuf[row*64 +col]))
  1217.       mPrintf("(%x)", peekBuf[row*64 +col]);
  1218.  
  1219.       }
  1220.  
  1221.     }
  1222.   #else
  1223.   printf("Disabled\n");
  1224.   #endif
  1225.  
  1226.   }
  1227. /*
  1228. * msgToDisk()
  1229. *
  1230. * This puts a message to the given disk file.
  1231. */
  1232. void msgToDisk(char *filename, char all, MSG_NUMBER id, SECTOR_ID loc,
  1233. UNS_16 size)
  1234.   {
  1235.   char *fn;
  1236.   long x;
  1237.   if( filename != NULL )
  1238.     {
  1239.     fn = GetDynamic(strLen(filename) + 5);
  1240.     strCpy(fn, filename);
  1241.     };
  1242.   id &= S_MSG_MASK;
  1243.   outFlag = OUTOK;
  1244.   if (redirect(filename))
  1245.     {
  1246.     if (!all)
  1247.       {
  1248.       if (findMessage(loc, id, TRUE))
  1249.         {
  1250.         if (size != 0)
  1251.           {
  1252.           totalBytes(&x, upfd);
  1253.           if (x > size * 1024)
  1254.             {
  1255.             undirect();
  1256.             FindNextFile(fn);
  1257.             rename(filename, fn);
  1258.             redirect(filename);
  1259.  
  1260.             }
  1261.  
  1262.           }
  1263.         printMessage(0);
  1264.  
  1265.         }
  1266.       else splitF(netLog, "bad findmessage!\n"    );
  1267.  
  1268.       }
  1269.     else
  1270.       {
  1271.       showMessages(OLDaNDnEW, FALSE, 0l, printMessage);
  1272.  
  1273.       }
  1274.     undirect();
  1275.  
  1276.     }
  1277.   free(fn);
  1278.  
  1279.   }
  1280. SListBase Errors =
  1281.   {
  1282.   NULL, NULL, NULL, free, NULL
  1283.  
  1284.   };
  1285. /*
  1286. * noteMessage()
  1287. *
  1288. * This function slots message into current room, delivers mail if necessary,
  1289. * handles net mail, Who Else stuff, mail forwarding, checkpointing, room
  1290. * archiving, etc etc etc etc etc.
  1291. */
  1292. void noteMessage(logBuffer *lBuf)
  1293.   {
  1294.   int logRover, size = 0;
  1295.   char *fn, *realfn;
  1296.   CheckPoint Cpt;
  1297.   FILE *fd;
  1298.   extern SListBase Arch_base;
  1299.   extern char     *ALL_LOCALS, *WRITE_ANY;
  1300.   void AssembleMessage();
  1301.   KillList(&FwdVortex);
  1302.   ArchiveMail = FALSE;
  1303.   ++cfg.newest;
  1304.   if (lBuf == &logBuf)
  1305.   logBuf.lbvisit[0] = cfg.newest;
  1306.   if (thisRoom != MAILROOM)
  1307.     {
  1308.     noteAMessage(roomBuf.msg, MSGSPERRM, cfg.newest, cfg.catSector);
  1309.     /* write it to disk:            */
  1310.     putRoom(thisRoom);
  1311.     noteRoom();
  1312.  
  1313.     }
  1314.   else
  1315.     {
  1316.     /* when in Mail... */
  1317.     /*
  1318.     * First, we handle the origin of this message in Mail>.  Note that
  1319.     * checking loggedIn handles both anonymous and incoming NET mail --
  1320.     * loggedIn is always false when in net mode, or should be!
  1321.     */
  1322.     if (loggedIn || lBuf != &logBuf)
  1323.       {
  1324.       noteAMessage(lBuf->lbMail, MAILSLOTS, cfg.newest, cfg.catSector);
  1325.       if (lBuf == &logBuf)
  1326.       putLog(&logBuf, thisLog);
  1327.       ArchiveMail = (strCmpU(cfg.SysopName, logBuf.lbname) == SAMESTRING);
  1328.  
  1329.       }
  1330.     /*
  1331.     * If there are overrides on delivery target, process them in
  1332.     * preference to the mbto and mbCC fields.
  1333.     */
  1334.     if (HasOverrides(&msgBuf))
  1335.       {
  1336.       RunList(&msgBuf.mbOverride, AddMail);
  1337.  
  1338.       }
  1339.     else if (msgBuf.mbaddr[0] ||
  1340.     strCmpU(msgBuf.mbto, lBuf->lbname) != SAMESTRING)
  1341.       {
  1342.       /* kinda silly, but .. */
  1343.       if (msgBuf.mbaddr[0] && inNet == NON_NET)
  1344.         {
  1345.         if (strCmpU(msgBuf.mbaddr, ALL_LOCALS) != SAMESTRING)
  1346.         AddNetMail(msgBuf.mbaddr, TRUE);
  1347.         else
  1348.           {
  1349.           for (logRover = 0; logRover < cfg.netSize; logRover++)
  1350.             {
  1351.             /* this code could be better optimized (speed) */
  1352.             getNet(logRover, &netBuf);
  1353.             if (netBuf.nbflags.in_use && netBuf.nbflags.local &&
  1354.             (netBuf.MemberNets & ALL_NETS))
  1355.             AddNetMail("", TRUE);
  1356.  
  1357.             }
  1358.  
  1359.           }
  1360.  
  1361.         }
  1362.       else if ((logRover = PersonExists(msgBuf.mbto)) == ERROR)
  1363.         {
  1364.         mPrintf("Internal error in mail (-%s-)!\n ", msgBuf.mbto);
  1365.         return ;
  1366.  
  1367.         }
  1368.       else if (logRover == cfg.MAXLOGTAB)
  1369.         {
  1370.         /* special recipient */
  1371.         if (strCmpU(msgBuf.mbto, "Citadel") == SAMESTRING)
  1372.           {
  1373.           if (!msgBuf.mbaddr[0])
  1374.             {
  1375.             /* Not netward bound?? */
  1376.             for (logRover = 0; logRover < cfg.MAXLOGTAB;
  1377.             logRover++)
  1378.             if (logRover != thisLog)
  1379.               {
  1380.               printf("Log %d\r", logRover); /* Notify sysop */
  1381.               getLog(&logTmp, logRover);
  1382.               if (logTmp.lbflags.L_INUSE)
  1383.                 {
  1384.                 noteAMessage(logTmp.lbMail, MAILSLOTS,
  1385.                 cfg.newest, cfg.catSector);
  1386.                 putLog(&logTmp, logRover);
  1387.  
  1388.                 }
  1389.  
  1390.               }
  1391.  
  1392.             }
  1393.  
  1394.           }
  1395.         else
  1396.           {
  1397.           AddMail(msgBuf.mbto);
  1398.  
  1399.           }
  1400.  
  1401.         }
  1402.       else
  1403.         {
  1404.         AddMail(msgBuf.mbto);
  1405.  
  1406.         }
  1407.  
  1408.       }
  1409.     if (inNet == NON_NET)
  1410.       {
  1411.       RunList(&msgBuf.mbCC, AddMail);
  1412.  
  1413.       }
  1414.     if (lBuf == &logBuf)
  1415.     fillMailRoom();                      /* update room also */
  1416.  
  1417.     }
  1418.   /* Finally, kill this list */
  1419.   KillList(&SysList);
  1420.   /* Checkpoint stuff - this HAS to be here before catSector is changed */
  1421.   Cpt.ltnewest = cfg.newest;
  1422.   Cpt.loc        = cfg.catSector;
  1423.   /* make message official:   */
  1424.   cfg.catSector   = mFile1.thisSector;
  1425.   cfg.catChar     = mFile1.thisChar;
  1426.   setUp(FALSE);
  1427.   if (roomBuf.rbflags.ARCHIVE == 1 ||
  1428.   (ArchiveMail && strLen(cfg.SysopArchive) != 0))
  1429.     {
  1430.     if (roomBuf.rbflags.ARCHIVE == 1)
  1431.     fn = SearchList(&Arch_base, NtoStrInit(thisRoom, "", 0, TRUE));
  1432.     else
  1433.     fn = cfg.SysopArchive;
  1434.     realfn = GetDynamic(strLen(fn) + 15);
  1435.     TranslateFilename(realfn, fn);
  1436.     if (fn == NULL)
  1437.       {
  1438.       sPrintf(msgBuf.mbtext, "Integrity problem with Archiving: %s.",
  1439.       roomBuf.rbname);
  1440.       aideMessage(NULL,FALSE);
  1441.  
  1442.       }
  1443.     else
  1444.       {
  1445.       if (roomBuf.rbflags.ARCHIVE == 1)
  1446.       size = GetArchSize(thisRoom);
  1447.       msgToDisk(realfn, FALSE, Cpt.ltnewest, Cpt.loc, size);
  1448.  
  1449.       }
  1450.     free(realfn);
  1451.  
  1452.     }
  1453.   msgBuf.mbaddr[0] = 0;
  1454.   msgBuf.mbto[0]   = 0;
  1455.   /* OK, so let's write out our checkpoint */
  1456.   if (inNet == NON_NET) /* this is strictly performance oriented */
  1457.   if ((fd = fopen(CHECKPT, WRITE_ANY)) != NULL)
  1458.     {
  1459.     fwrite(&Cpt, sizeof Cpt, 1, fd);
  1460.     fclose(fd);
  1461.  
  1462.     }
  1463.   if (GetFirst(&Errors) != NULL)
  1464.     {
  1465.     ZeroMsgBuffer(&msgBuf);
  1466.     RunList(&Errors, AssembleMessage);
  1467.     KillList(&Errors);
  1468.     CleanEnd(msgBuf.mbtext);
  1469.     aideMessage("Net Aide",FALSE);
  1470.  
  1471.     }
  1472.  
  1473.   }
  1474. /*
  1475. * AssembleMessage()
  1476. *
  1477. * This adds a submessage to a message. (?)
  1478. */
  1479. void AssembleMessage(char *str)
  1480.   {
  1481.   sPrintf(lbyte(msgBuf.mbtext), "%s\n\n", str);
  1482.  
  1483.   }
  1484. /*
  1485. * AddMail()
  1486. *
  1487. * This function should deliver mail to the named person.
  1488. */
  1489. void AddMail(char *DaPerson)
  1490.   {
  1491.   label person;
  1492.   char  system[(NAMESIZE * 2) + 10];
  1493.   int   slot;
  1494.   char *InternalError = "Internal error, couldn't identify '%s'\n ";
  1495.   if (cfg.BoolFlags.debug)
  1496.       splitF(netLog,"AddMail(%s)\n",DaPerson);
  1497.   switch (SepNameSystem(DaPerson, person, system, &netBuf))
  1498.     {
  1499.     case IS_SYSTEM:
  1500.     case SYSTEM_IS_US:
  1501.     if (!NetValidate(TRUE)) return;
  1502.     AddNetMail(system, TRUE);
  1503.     break;
  1504.     case BAD_FORMAT:
  1505.     if (inNet == NON_NET) mPrintf(InternalError, DaPerson);
  1506.     break;
  1507.     case NO_SYSTEM:
  1508.     if (inNet == NON_NET) mPrintf(InternalError, system);
  1509.     break;
  1510.     case NOT_SYSTEM:
  1511.     if (strCmpU(DaPerson, "sysop") == SAMESTRING)
  1512.       {
  1513.       ArchiveMail = TRUE;
  1514.       if ((slot = findPerson(cfg.SysopName, &logTmp)) == ERROR)
  1515.         {
  1516.         getRoom(AIDEROOM);
  1517.         /* enter in Aide> room -- 'sysop' is special */
  1518.         noteAMessage(roomBuf.msg, MSGSPERRM,
  1519.         cfg.newest, cfg.catSector);
  1520.         /* write it to disk:        */
  1521.         putRoom(AIDEROOM);
  1522.         noteRoom();
  1523.         getRoom(MAILROOM);
  1524.  
  1525.         }
  1526.       else
  1527.       MailWork(slot);
  1528.  
  1529.       }
  1530.     else if ((slot = findPerson(DaPerson, &logTmp)) == ERROR)
  1531.       {
  1532.       if (inNet == NON_NET)
  1533.       mPrintf(InternalError, DaPerson);
  1534.       else
  1535.       splitF(netLog, "No recipient: %s\n", DaPerson);
  1536.  
  1537.       }
  1538.     else
  1539.       {
  1540.       MailWork(slot);
  1541.  
  1542.       }
  1543.  
  1544.     }
  1545.  
  1546.   }
  1547. /*
  1548. * MailWork()
  1549. *
  1550. * This function is central to Mail delivery, and handles forwarding of both
  1551. * sorts.
  1552. */
  1553. void MailWork(int slot)
  1554.   {
  1555.   logBuffer lBuf;
  1556.   if( logNetResults )
  1557.   if (inNet != NON_NET) splitF(netLog, "Mail for %s.\n", logTmp.lbname);
  1558.   noteAMessage(logTmp.lbMail, MAILSLOTS, cfg.newest, cfg.catSector);
  1559.   if (strCmpU(cfg.SysopName, logTmp.lbname) == SAMESTRING)
  1560.   ArchiveMail = TRUE;
  1561.   NetForwarding(&logTmp);
  1562.   putLog(&logTmp, slot);
  1563.   /* so we can't redeliver to this account */
  1564.   AddData(&FwdVortex, strdup(logTmp.lbname), NULL, FALSE);
  1565.   /* now check for forwarding to a local address */
  1566.   initLogBuf(&lBuf);
  1567.   LocalForwarding(FindLocalForward(logTmp.lbname), &lBuf);
  1568.   killLogBuf(&lBuf);
  1569.  
  1570.   }
  1571. /*
  1572. * NetForwarding()
  1573. *
  1574. * This handles network forwarding.
  1575. */
  1576. void NetForwarding(logBuffer *lBuf)
  1577.   {
  1578.   int cost;
  1579.   ForwardMail *data;
  1580.   label domain;
  1581.   char *system;
  1582.   extern SListBase MailForward;
  1583.   /* Has forwarding address?  */
  1584.   if ((data = SearchList(&MailForward, lBuf->lbname)) != NULL &&
  1585.   lBuf->lbflags.NET_PRIVS)
  1586.     {
  1587.     system = strdup(data->System);
  1588.     if (ReqNodeName("", system, domain, FALSE, TRUE, FALSE, FALSE,
  1589.     FALSE, &netTemp))
  1590.       {
  1591.       if (domain[0] == 0)
  1592.       cost = !netTemp.nbflags.local;
  1593.       else
  1594.       cost = FindCost(domain);
  1595.       free(system);
  1596.       system = strdup(data->System);
  1597.       if (lBuf->credit >= cost)
  1598.         {
  1599.         AddData(&msgBuf.mbInternal, strdup(lBuf->lbname), NULL, FALSE);
  1600.         AddNetMail(system, FALSE);
  1601.         KillList(&msgBuf.mbInternal);
  1602.  
  1603.         }
  1604.  
  1605.       }
  1606.     free(system);
  1607.  
  1608.     }
  1609.  
  1610.   }
  1611. /*
  1612. * LocalForwarding()
  1613. *
  1614. * This handles forwarding to a local account.  Since multiple forwarding
  1615. * may* be setup (seems unlikely), this is recursive.  A list is kept of
  1616. * recipients of mail in order to avoid both duplicate deliveries and
  1617. * infinite forwarding vortexes.
  1618. */
  1619. void LocalForwarding(char *name, logBuffer *workBuf)
  1620.   {
  1621.   int   slot;
  1622.   /* if this is NULL then there is no more forwarding to do */
  1623.   if (name == NULL) return;
  1624.   /* see if this account has already received the mail */
  1625.   if (SearchList(&FwdVortex, name) != NULL) return;
  1626.   if ((slot = findPerson(name, workBuf)) == ERROR)
  1627.   return;               /* implies an outofdate account */
  1628.   /* OK, save the message */
  1629.   noteAMessage(workBuf->lbMail, MAILSLOTS, cfg.newest, cfg.catSector);
  1630.   /* check the network forwarding for this account */
  1631.   NetForwarding(workBuf);
  1632.   putLog(workBuf, slot);
  1633.   AddData(&FwdVortex, strdup(name), NULL, FALSE);
  1634.   LocalForwarding(FindLocalForward(name), workBuf);
  1635.  
  1636.   }
  1637. /*
  1638. * AddNetMail()
  1639. *
  1640. * This should manage adding mail to a net system.
  1641. */
  1642. void AddNetMail(char *system, char CreditSender)
  1643.   {
  1644.   int cost, slot;
  1645.   logBuffer *lBuf;
  1646.   char isdomain = FALSE, *domain, *System;
  1647.   /*
  1648.   * sometimes system is mbaddr, which is not good, because later on down
  1649.   * the line we call findMessage, which will result in (unfortunately)
  1650.   * mbaddr being overwritten.  So we dup system.
  1651.   */
  1652.   if (cfg.BoolFlags.debug)
  1653.       splitF(netLog,"AddNetMail(%s,%s)\n"
  1654.              ,system,(CreditSender ? "TRUE" : "FALSE"));
  1655.  
  1656.   System = strdup(system);
  1657.   if (CreditSender) lBuf = &logBuf;
  1658.   else           lBuf = &logTmp;
  1659.   if (strLen(System))
  1660.     {
  1661.     isdomain = (domain = strchr(System, '_')) != NULL;
  1662.     if (!isdomain)
  1663.       {
  1664.       slot = searchNameNet(System, &netTemp);
  1665.       cost = !netTemp.nbflags.local;
  1666.  
  1667.       }
  1668.     else
  1669.       {
  1670.       *domain++ = 0;
  1671.       NormStr(domain);
  1672.       NormStr(System);
  1673.       cost = FindCost(domain);
  1674.  
  1675.       }
  1676.  
  1677.     }
  1678.   else
  1679.     {
  1680.     slot = thisNet;
  1681.     cost = 0;
  1682.     getNet(thisNet, &netTemp);  /* &L mail */
  1683.     system = netTemp.netName;
  1684.  
  1685.     }
  1686.   if (cost > lBuf->credit && !(lBuf == &logBuf && HalfSysop()))
  1687.     {
  1688.     free(System);
  1689.     return ;
  1690.  
  1691.     }
  1692.   lBuf->credit -= cost;
  1693.   if (SearchList(&SysList, system) == NULL)
  1694.     {
  1695.     AddData(&SysList, strdup(system), NULL, FALSE);
  1696.     netMailOut(isdomain, System, domain, TRUE, slot);
  1697.  
  1698.     }
  1699.   free(System);
  1700.   if (lBuf->credit < 0)
  1701.   lBuf->credit = 0;
  1702.  
  1703.   }
  1704. /*
  1705. * noteAMessage()
  1706. *
  1707. * This should add a message pointer to any room.
  1708. */
  1709. void noteAMessage(theMessages *base, int slots, MSG_NUMBER id, SECTOR_ID loc)
  1710.   {
  1711.   int  i;
  1712.   /* store into current room: */
  1713.   /* slide message pointers down to make room for this one:      */
  1714.   for (i = 0;  i < slots - 1;  i++)
  1715.     {
  1716.     base[i].rbmsgLoc  = base[i+1].rbmsgLoc;
  1717.     base[i].rbmsgNo   = base[i+1].rbmsgNo ;
  1718.  
  1719.     }
  1720.   /* slot this message in:      */
  1721.   base[slots-1].rbmsgNo     = id ;
  1722.   base[slots-1].rbmsgLoc    = loc;
  1723.  
  1724.   }
  1725. /*
  1726. * printMessage()
  1727. *
  1728. * This prints the indicated message on modem & console.
  1729. */
  1730. char printMessage(int status)
  1731.   {
  1732.   int  moreFollows;
  1733.   int  oldTermWidth;
  1734.   int  strip;
  1735.   extern char CCfirst, CCOutFlag;
  1736.   if( termWidth < 40 ) termWidth = 40;   /* force a semi-readable size */
  1737.   oldTermWidth = termWidth;
  1738.   if (outPut == DISK)
  1739.     {
  1740.     termWidth = 80;
  1741.  
  1742.     }
  1743.   doCR();
  1744.   mPrintf("%s", formHeader());
  1745.   doCR();
  1746.   /* Print out who is on the CC list for this message. */
  1747.   ShowCC(SCREEN);
  1748.   EOP = TRUE;
  1749.   if (status == 0)
  1750.     {
  1751.     if (outFlag != OUTSKIP && outFlag != OUTNEXT)
  1752.     while (1)
  1753.       {
  1754.       moreFollows     = dGetWord(msgBuf.mbtext, 150);
  1755.       /* strip control Ls out of the output                */
  1756.       for (strip = 0; msgBuf.mbtext[strip] != 0; strip++)
  1757.         if(msgBuf.mbtext[strip] == 0x0C ||
  1758.            msgBuf.mbtext[strip] == 0x0E ||
  1759.            msgBuf.mbtext[strip] == 0x0F ) msgBuf.mbtext[strip] = ' ';
  1760.       if (msgBuf.mbtext[strip] == SPECIAL && !logBuf.lbflags.ANSI) msgBuf.mbtext[strip] = ' ';
  1761.       putWord(msgBuf.mbtext);
  1762.       if (!(moreFollows  &&  !mAbort()))
  1763.         {
  1764.         if (outFlag == OUTNEXT)          /* If <N>ext, extra line */
  1765.         doCR();
  1766.         break;
  1767.  
  1768.         }
  1769.  
  1770.       }
  1771.  
  1772.     }
  1773.   else
  1774.     {
  1775.     mFormat(msgBuf.mbtext);
  1776.  
  1777.     }
  1778.   if (EndWithCR) doCR();
  1779.   termWidth = oldTermWidth;
  1780.   return TRUE;
  1781.  
  1782.   }
  1783. /*
  1784. * pullIt()
  1785. *
  1786. * This is a sysop special to remove or otherwise manipulate a message in a
  1787. * room.
  1788. */
  1789. char pullIt(int m)
  1790.   {
  1791.   char  toReturn;
  1792.   char  answer;
  1793.   char *DelOpts[] =
  1794.     {
  1795.     "Delete message\n", "Move message\n", "Copy message\n",
  1796.     "Abort\n", " ", ""
  1797.  
  1798.     };
  1799.   /* confirm that we're removing the right one:  */
  1800.   outFlag = OUTOK;
  1801.   if (findMessage(roomBuf.msg[m].rbmsgLoc, roomBuf.msg[m].rbmsgNo, TRUE))
  1802.   printMessage(0);
  1803.   if (roomBuf.rbflags.SHARED && !msgBuf.mboname[0])
  1804.     {
  1805.     ExtraOption(DelOpts, "Net message");
  1806.  
  1807.     }
  1808.   RegisterThisMenu(NULL, DelOpts);
  1809.   do
  1810.     {
  1811.     outFlag = IMPERVIOUS;
  1812.     TellRoute();
  1813.     mPrintf("\n <D>elete <M>ove <C>opy <A>bort");
  1814.     if (roomBuf.rbflags.SHARED && !msgBuf.mboname[0])
  1815.     mPrintf(" <N>et");
  1816.     mPrintf("? (D/M/C/A%s) ",
  1817.     (roomBuf.rbflags.SHARED && !msgBuf.mboname[0]) ? "/N" : "");
  1818.     switch ((answer = GetMenuChar()))
  1819.       {
  1820.       case 'D':
  1821.       if (deleteMessage(m))
  1822.       return DELETED;
  1823.       break;
  1824.       case 'M':
  1825.       case 'C':
  1826.       if (moveMessage(answer, m, &toReturn))
  1827.       return toReturn;
  1828.       break;
  1829.       case 'A':
  1830.       return NO_CHANGE;
  1831.       case 'N':
  1832.       return MakeNetted(m);
  1833.  
  1834.       }
  1835.  
  1836.     }
  1837.   while (onLine());
  1838.   return DELETED;
  1839.  
  1840.   }
  1841. /*
  1842. * putMessage()
  1843. *
  1844. * This function stores a message to disk.
  1845. * Always called before noteMessage() -- newest not ++ed yet.
  1846. * Returns: TRUE on successful save, else FALSE
  1847. */
  1848. char putMessage(logBuffer *lBuf)
  1849.   {
  1850.   char *s;
  1851.   extern char *ALL_LOCALS, *WRITE_LOCALS, CCOutFlag;
  1852.   extern char *R_SH_MARK, *LOC_NET, *NON_LOC_NET;
  1853.   void dLine();
  1854.   startAt(msgfl, &mFile1, cfg.catSector, cfg.catChar);
  1855.   /* tell putMsgChar where to write   */
  1856.   if (cfg.BoolFlags.mirror)
  1857.   startAt(msgfl2, &mFile2, cfg.catSector, cfg.catChar);
  1858.   putMsgChar(0xFF);              /* start-of-message             */
  1859.   /* write message ID */
  1860.   dPrintf("%lu", cfg.newest + 1);
  1861.   if (inNet != NON_NET ||
  1862.   (!roomBuf.rbflags.ANON || strCmp(msgBuf.mbdate, "****") != SAMESTRING))
  1863.     {
  1864.     /* write date:       */
  1865.     if (msgBuf.mbdate[0])
  1866.       {
  1867.       dPrintf("D%s", msgBuf.mbdate);
  1868.  
  1869.       }
  1870.     else
  1871.       {
  1872.       dPrintf("D%s", formDate());
  1873.  
  1874.       }
  1875.     /* write time:       */
  1876.     if (msgBuf.mbtime[0])
  1877.       {
  1878.       dPrintf("C%s", msgBuf.mbtime);
  1879.  
  1880.       }
  1881.     else
  1882.       {
  1883.       dPrintf("C%s", Current_Time());
  1884.  
  1885.       }
  1886.     /* write author's name out:  */
  1887.     if (msgBuf.mbauth[0])
  1888.       {
  1889.       dPrintf("A%s", msgBuf.mbauth);
  1890.  
  1891.       }
  1892.  
  1893.     }
  1894.   else
  1895.     {
  1896.     dPrintf("D****");
  1897.  
  1898.     }
  1899.   /* write room name out:            */
  1900.   dPrintf("R%s", msgBuf.mbroom[0] ? msgBuf.mbroom : roomBuf.rbname);
  1901.   if (msgBuf.mbto[0])
  1902.     {
  1903.     /* private message -- write addressee   */
  1904.     dPrintf("T%s", msgBuf.mbto);
  1905.  
  1906.     }
  1907.   if (msgBuf.mboname[0])
  1908.     {
  1909.     dPrintf("N%s", msgBuf.mboname);
  1910.  
  1911.     }
  1912.   if (msgBuf.mbdomain[0])
  1913.     {
  1914.     dPrintf("X%s", msgBuf.mbdomain);
  1915.  
  1916.     }
  1917.   if (msgBuf.mborig[0])
  1918.     {
  1919.     dPrintf("O%s", msgBuf.mborig);
  1920.  
  1921.     }
  1922.   /* this convolution lets us retrace routing for shared rooms */
  1923.   if (msgBuf.mbaddr[0])
  1924.     {
  1925.     /* net message routing */
  1926.     /* generated by user */
  1927.     if (inNet == NON_NET || (strCmp(msgBuf.mbaddr, LOC_NET) != SAMESTRING &&
  1928.     strCmp(msgBuf.mbaddr, NON_LOC_NET) != SAMESTRING))
  1929.     dPrintf("Q%s", wrNetId(msgBuf.mbaddr));
  1930.     else            /* saving a net message              */
  1931.     dPrintf("Q%s%d", wrNetId(msgBuf.mbaddr), thisNet);
  1932.     if (strCmpU(msgBuf.mbaddr, R_SH_MARK  ) == SAMESTRING ||
  1933.     strCmpU(msgBuf.mbaddr, NON_LOC_NET) == SAMESTRING)
  1934.     roomTab[thisRoom].rtlastNet = cfg.newest + 1;
  1935.  
  1936.     }
  1937.   if (msgBuf.mbsrcId[0])
  1938.     {
  1939.     dPrintf("S%s", msgBuf.mbsrcId);
  1940.  
  1941.     }
  1942.   if (msgBuf.mbOther[0])
  1943.     {
  1944.     dPrintf("P%s", msgBuf.mbOther);
  1945.  
  1946.     }
  1947.   if (msgBuf.mbreply[0])
  1948.   dPrintf("w%s", msgBuf.mbreply);               /* back ptr */
  1949.   /* This writes out the list of CC people to the message base. */
  1950.   /* Note we don't usually write Overrides to the message base. */
  1951.   CCOutFlag = MSGBASE;
  1952.   RunList(&msgBuf.mbCC, DisplayCC);
  1953.   /* save foreign fields */
  1954.   RunList(&msgBuf.mbForeign, dLine);
  1955.   /* write message text by hand because it would overrun dPrintf buffer: */
  1956.   putMsgChar('M');    /* M-for-message.  */
  1957.   for (s = msgBuf.mbtext;  *s;  s++) putMsgChar(*s);
  1958.   putMsgChar(0);         /* null to end text     */
  1959.   flushMsgBuf();
  1960.   noteMessage(lBuf);
  1961.   return  TRUE;
  1962.  
  1963.   }
  1964. /*
  1965. * dLine()
  1966. *
  1967. * This prints a line to the msg base, including the NULL byte.
  1968. */
  1969. void dLine(char *garp)
  1970.   {
  1971.   do
  1972.   putMsgChar(*garp);
  1973.   while (*garp++);
  1974.  
  1975.   }
  1976. /*
  1977. * netMailOut()
  1978. *
  1979. * This should put the mail pointer and number into temp file for local mail,
  1980. * or will set up the temp file for routed mail.
  1981. *  SOMEDAY MOVE THIS INTO NETMISC!
  1982. */
  1983. void netMailOut(char isdomain, char *system, char *domain, char MsgBase,
  1984. int slot)
  1985.   {
  1986.   FILE  *fd;
  1987.   label temp, id = "";
  1988.   int    result;
  1989.   DOMAIN_FILE fn;
  1990.   extern char *APPEND_ANY, *WRITE_ANY;
  1991.   struct        netMLstruct buf;
  1992.   if (cfg.BoolFlags.debug)
  1993.      splitF(netLog," netMailOut(%s,%s,%s,%s.%d)\n"
  1994.             ,(isdomain ? "TRUE" : "FALSE")
  1995.             ,system, domain, (MsgBase ? "TRUE" : "FALSE"));
  1996.   if (isdomain)
  1997.     {
  1998.     if ((result = DomainMailFileName(fn, domain, id, system))==LOCALROUTE)
  1999.       {
  2000.       isdomain = FALSE;
  2001.       slot = searchNameNet(system, &netTemp);
  2002.  
  2003.       }
  2004.  
  2005.     }
  2006.   else
  2007.     {
  2008.     result = LOCALROUTE;
  2009.     if (!MsgBase) slot = searchNameNet(system, &netTemp);
  2010.     if (slot == ERROR)
  2011.       {
  2012.       splitF(netLog, "BUG!  Slot is -1 for %s.\n", system);
  2013.       return;
  2014.  
  2015.       }
  2016.  
  2017.     }
  2018.   if (!isdomain && MsgBase && DirectRoute(&netTemp) && inNet == NON_NET)
  2019.     {
  2020.     sPrintf(temp, "%d.ml", slot);
  2021.     makeSysName(fn, temp, &cfg.netArea);
  2022.     if ((fd = safeopen(fn, APPEND_ANY)) == NULL)
  2023.       {
  2024.       crashout("putMessage -- couldn't open direct mail file!");
  2025.  
  2026.       }
  2027.     buf.ML_id  = cfg.newest;
  2028.     buf.ML_loc = cfg.catSector;
  2029.     putMLNet(fd, buf);
  2030.     fclose(fd);
  2031.     netTemp.nbflags.normal_mail = TRUE;
  2032.     putNet(slot, &netTemp);
  2033.  
  2034.     }
  2035.   else
  2036.     {
  2037.     MakeIntoRouteMail(result, fn, isdomain, system, domain, MsgBase, slot);
  2038.  
  2039.     }
  2040.  
  2041.   }
  2042. /*
  2043. * MakeIntoRouteMail()
  2044. *
  2045. * This will set up a Rx.x file.
  2046. */
  2047. void MakeIntoRouteMail(int result, DOMAIN_FILE fn
  2048.                        , char isdomain, char *system
  2049.                        , char *domain, char OriginIsMsgBase, int slot)
  2050.   {
  2051.   int    index, route;
  2052.   label temp;
  2053.   label name;
  2054.   char  For[(2 * NAMESIZE) + 10];
  2055.   void  (*TempPrint)(char *fmt, ...);
  2056.   extern void (*NetPrintTarget)(char *fmt, ...);
  2057.   extern int  (*ToFileWork)();
  2058.   extern char PrTransmit;
  2059.  
  2060.   if (cfg.BoolFlags.debug)
  2061.      splitF(netLog,"MakeIntoRouteMail(%d, %s, %s, %s, %s, %s, %d)\n"
  2062.             , result, fn, (isdomain ? "TRUE" : "FALSE")
  2063.             , system, domain, ( OriginIsMsgBase ? "TRUE" : "FALSE")
  2064.             , slot);
  2065.  
  2066.  
  2067.   route = (netTemp.nbRoute == -1) ? slot : netTemp.nbRoute;
  2068.   if (cfg.BoolFlags.debug) splitF(netLog, "route mail: system(%s) domain(%s) route(%d) \n",system,domain,route);
  2069.   if (result == LOCALROUTE)
  2070.     {
  2071.     strCpy(name, UseNetAlias(netTemp.netName, TRUE));
  2072.     index = FindRouteIndex(route);
  2073.     sPrintf(temp, "R%d.%d", route, index);
  2074.     makeSysName(fn, temp, &cfg.netArea);
  2075.     strCpy(For, netTemp.netName);
  2076.  
  2077.     }
  2078.   else sPrintf(For, "%s _ %s", system, domain);
  2079.   if (cfg.BoolFlags.debug) splitF(netLog, "route mail filename is %s for %s\n",fn, For);
  2080.   if ((upfd = safeopen(fn, WRITE_ANY)) == NULL)
  2081.     {
  2082.     splitF(netLog,"filename is -%s-\n", fn);
  2083.     crashout("couldn't open route mail file!");
  2084.  
  2085.     }
  2086.   ToFile("%-20s", (isdomain) ? " " : netTemp.netId);
  2087.   ToFile("%-20s", (isdomain) ? system : name);
  2088.   if (OriginIsMsgBase)
  2089.   findMessage(cfg.catSector, cfg.newest, FALSE);  /* we use false here */
  2090.   TempPrint = NetPrintTarget;
  2091.   NetPrintTarget = ToFile;
  2092.   StartEncode(putFLChar);
  2093.   ToFileWork = Encode;
  2094.   PrTransmit = FALSE;
  2095.   prNetStyle(!OriginIsMsgBase, Encode, OriginIsMsgBase, For);
  2096.   PrTransmit = TRUE;
  2097.   StopEncode();
  2098.   ToFileWork = putFLChar;
  2099.   NetPrintTarget = TempPrint;
  2100.   fclose(upfd);
  2101.   if (!isdomain)
  2102.     {
  2103.     if (inNet == NON_NET || thisNet != route)
  2104.       {
  2105.       getNet(route, &netTemp);
  2106.       netTemp.nbflags.HasRouted = TRUE;
  2107.       netTemp.nbHiRouteInd = index + 1;
  2108.       putNet(route, &netTemp);
  2109.  
  2110.       }
  2111.     else
  2112.       {
  2113.       netBuf.nbflags.HasRouted = TRUE;
  2114.       netBuf.nbHiRouteInd = index + 1;  /* saved by net stuff */
  2115.  
  2116.       }
  2117.  
  2118.     }
  2119.   else DomainFileAddResult(domain, system, "", DOMAIN_SUCCESS);
  2120.  
  2121.   }
  2122. /*
  2123. * putMsgChar()
  2124. *
  2125. * This writes successive message chars to disk.
  2126. *
  2127. * Globals:      thisChar=       thisSector=
  2128. * Returns:      ERROR if problems else TRUE
  2129. */
  2130. int putMsgChar(int c)
  2131.   {
  2132.   int  toReturn;
  2133.   int  count1, count2;
  2134.   toReturn = TRUE;
  2135.   count1 = doActualWrite(msgfl, &mFile1, c);
  2136.   if (cfg.BoolFlags.mirror)
  2137.     {
  2138.     count2 = doActualWrite(msgfl2, &mFile2, c);
  2139.     if (count1 != count2) printf("Mirror msg count discrepancy!");
  2140.  
  2141.     }
  2142.   if (count1)
  2143.   logBuf.lbvisit[(MAXVISIT-1)]    = ++cfg.oldest;
  2144.   return toReturn;
  2145.  
  2146.   }
  2147. /*
  2148. * putWord()
  2149. *
  2150. * This function writes one word to modem & console.
  2151. */
  2152. void putWord(char *st)
  2153.   {
  2154.   char *s;
  2155.   int  newColumn;
  2156.   if( termWidth < 40 ) termWidth = 40;   /* force a semi-readable size */
  2157.   for (newColumn = crtColumn, s = st;  *s; s++)
  2158.     {
  2159.     /* make special adjustment for escape sequences */
  2160.     if( *s == SPECIAL ) newColumn -= ( *(s+2) == '0' ? 4 : 8);
  2161.     if (*s != TAB)
  2162.       {
  2163.       if (*s == '\b') newColumn--;
  2164.       else if (*s == '\n')
  2165.         {
  2166.         if (*(s+1) == '\n' || *(s+1) == ' ')
  2167.         newColumn = 1;
  2168.         else ++newColumn;
  2169.  
  2170.         }
  2171.       else ++newColumn;
  2172.  
  2173.       }
  2174.     else            while (++newColumn % 8);
  2175.  
  2176.     }
  2177.   if (newColumn > termWidth)
  2178.     {
  2179.     doCR();
  2180.     if (*st == '\n' && *(st+1) != '\n' && *(st+1) != ' ' && *(st+1))
  2181.     st++;
  2182.  
  2183.     }
  2184.   BufferingOn();
  2185.   for (;  *st;  st++)
  2186.     {
  2187.     #ifdef OLD_STYLE
  2188.     if (*st != TAB) ++crtColumn;
  2189.     else            while (++crtColumn % 8);
  2190.     #else
  2191.     #ifdef NEEDED
  2192.     if (*st != TAB)
  2193.       {
  2194.       if (*st == '\b') crtColumn--;
  2195.       else ++crtColumn;
  2196.  
  2197.       }
  2198.     else            while (++crtColumn % 8);
  2199.     #endif
  2200.     #endif
  2201.     /* worry about words longer than a line:    */
  2202.     if( *st == SPECIAL ) crtColumn -= ( *(s+2) == '0' ? 4 : 8);
  2203.     if (crtColumn > termWidth) doCR();
  2204.     if (*st == '\n' && EOP)
  2205.     doCR();
  2206.     else if (prevChar!=NEWLINE  ||  (*st > ' '))
  2207.       {
  2208.       oChar(*st);
  2209.       if (*st > ' ') EOP = FALSE;
  2210.  
  2211.       }
  2212.     else
  2213.       {
  2214.       /* end of paragraph: */
  2215.       if (outFlag == OUTPARAGRAPH)
  2216.         {
  2217.         outFlag = OUTOK;
  2218.  
  2219.         }
  2220.       doCR();
  2221.       if (*st == '\n' && !EOP) doCR();
  2222.       else oChar(*st);
  2223.       EOP = TRUE;
  2224.  
  2225.       }
  2226.  
  2227.     }
  2228.   BufferingOff();
  2229.  
  2230.   }
  2231. /*
  2232. * showMessages()
  2233. *
  2234. * This function will try to print a roomfull of messages.
  2235. */
  2236. char pause_whichMess;   /* kludge to avoid major problems */
  2237.  
  2238. int showMessages(char whichMess, char revOrder, MSG_NUMBER LastMsg,
  2239. char (*Style)(int i))
  2240.   {
  2241.   int           i, start, finish, increment, MsgCount = 0, result;
  2242.   MSG_NUMBER    lowLim, highLim, msgNo;
  2243.   char  pulled, PEUsed = FALSE, LoopIt;
  2244.   pause_whichMess = whichMess;
  2245.   setUp(FALSE);
  2246.   /* Don't need to check net status 'cuz netMail is sent differently. */
  2247.   if (thisRoom == MAILROOM && !loggedIn)
  2248.     {
  2249.     printHelp("POLICY.HLP");
  2250.     return 1;
  2251.  
  2252.     }
  2253.   if (TransProtocol == ASCII && inNet == NON_NET)
  2254.     Output_Citadel_Message("HOTHLP",NULL,NULL,NULL);
  2255.   /* This shouldn't bother the net. */
  2256.   if (whichIO != CONSOLE && thisRoom == MAILROOM) echo = CALLER;
  2257.   SetShowLimits(revOrder, &start, &finish, &increment);
  2258.   if (Showing == WHATEVER) Showing = MSGS;
  2259.   switch (whichMess)
  2260.     {
  2261.     case NEWoNLY:
  2262.     lowLim  = LastMsg + 1l;
  2263.     highLim = cfg.newest;
  2264.     if (inNet == NON_NET && !revOrder && TransProtocol == ASCII &&
  2265.     thisRoom != MAILROOM && oldToo)
  2266.       {
  2267.       for (i = MSGSPERRM - 1; i != -1; i--)
  2268.       if (lowLim > roomBuf.msg[i].rbmsgNo &&
  2269.       roomBuf.msg[i].rbmsgNo >= cfg.oldest)
  2270.       break;
  2271.       if (i != -1)
  2272.         {
  2273.         LoopIt = TRUE;
  2274.         while (i != -1 && LoopIt)
  2275.           {
  2276.           LoopIt = FALSE;
  2277.           findMessage(roomBuf.msg[i].rbmsgLoc,
  2278.           roomBuf.msg[i].rbmsgNo, TRUE);
  2279.           (*Style)(1);
  2280.           /* Pause-Enter for the last old on new feature */
  2281.           if (MsgStreamEnter)
  2282.             {
  2283.             LoopIt = TRUE;
  2284.             PEUsed = TRUE;
  2285.             if (InterruptMessage()) --i;
  2286.  
  2287.             }
  2288.  
  2289.           }
  2290.  
  2291.         }
  2292.  
  2293.       }
  2294.     break;
  2295.     case OLDaNDnEW:
  2296.     lowLim  = cfg.oldest;
  2297.     highLim = cfg.newest;
  2298.     break;
  2299.     case OLDoNLY:
  2300.     lowLim  = cfg.oldest;
  2301.     highLim = LastMsg;
  2302.     break;
  2303.  
  2304.     }
  2305.   /* stuff may have scrolled off system unseen, so: */
  2306.   if (cfg.oldest  > lowLim)
  2307.     {
  2308.     lowLim = cfg.oldest;
  2309.  
  2310.     }
  2311.   /*
  2312.   * We'll increment this loop at the end.  Doing so eases the job of
  2313.   * implementing the Pause-E option.
  2314.   */
  2315.   for (i = start; i != finish && (onLine() || inNet == NET_CACHE); )
  2316.     {
  2317.     if (outFlag != OUTOK)
  2318.       {
  2319.       if (outFlag == OUTNEXT || outFlag == OUTPARAGRAPH)
  2320.       outFlag = OUTOK;
  2321.       else if (outFlag == OUTSKIP)
  2322.         {
  2323.         echo = BOTH;
  2324.         return MsgCount;
  2325.  
  2326.         }
  2327.  
  2328.       }
  2329.     /* first get the REAL msgNo -- this is a kludge, replace next m. r. */
  2330.     msgNo = (roomBuf.msg[i].rbmsgNo & S_MSG_MASK);
  2331.     /*
  2332.     * Now check to see if msg is in "to be read" range, OR if we are
  2333.     * reading New AND the message is marked as SKIPPED (only happens in
  2334.     * Mail).  Note at the moment we're not going to worry about net
  2335.     * mode -- we don't use this loop for sending Mail, although we do
  2336.     * for other rooms.
  2337.     */
  2338.     if (
  2339.     (msgNo >= lowLim && highLim >= msgNo) ||
  2340.     (whichMess == NEWoNLY && msgNo != roomBuf.msg[i].rbmsgNo &&
  2341.     msgNo > cfg.oldest)
  2342.     )
  2343.       {
  2344.       if (findMessage(roomBuf.msg[i].rbmsgLoc, msgNo, TRUE))
  2345.         {
  2346.         ReverseMessage = FALSE;
  2347.         if ((*Style)(0))
  2348.           {
  2349.           /* successful print? */
  2350.           MsgCount++;
  2351.           /**
  2352.             Pull current message from room if flag set
  2353.           **/
  2354.           if (pullMessage)
  2355.             {
  2356.             pullMessage = FALSE;
  2357.             pulled = pullIt(i);
  2358.             outFlag = OUTOK;
  2359.             switch (pulled)
  2360.               {
  2361.               case NO_CHANGE:
  2362.               break;
  2363.               case DELETED:
  2364.               if (revOrder)   i++;
  2365.               break;
  2366.               case NETTED:
  2367.               if (!revOrder)   i--;
  2368.               break;
  2369.  
  2370.               }
  2371.  
  2372.             }
  2373.           else   pulled = FALSE;
  2374.           /**
  2375.             Reverse Order of Read
  2376.           **/
  2377.           if (ReverseMessage)
  2378.             {
  2379.             revOrder = !revOrder;
  2380.             SetShowLimits(revOrder, &start, &finish, &increment);
  2381.  
  2382.             }
  2383.           /**
  2384.             Journal Message
  2385.           **/
  2386.           if (journalMessage)
  2387.             {
  2388.             msgToDisk(NULL, FALSE, msgNo, roomBuf.msg[i].rbmsgLoc, 0);
  2389.             journalMessage = FALSE;
  2390.  
  2391.             }
  2392.           /**
  2393.             Pause-E option
  2394.           **/
  2395.           if (MsgStreamEnter)
  2396.             {
  2397.             if (thisRoom == MAILROOM)
  2398.               {
  2399.               ShowReply(i);
  2400.               MsgStreamEnter = FALSE;
  2401.               Showing = MSGS;
  2402.               outFlag = OUTOK;  /* so we can Pause later */
  2403.  
  2404.               }
  2405.             else
  2406.               {
  2407.               PEUsed = TRUE;
  2408.               if (InterruptMessage()) i--;
  2409.  
  2410.               }
  2411.             continue;   /* skip the increment - reprint msg */
  2412.  
  2413.             }
  2414.           if (
  2415.           Showing == MSGS         && outFlag != OUTSKIP  /* so we can <S>top Mail */
  2416.           && !pulled              && thisRoom  == MAILROOM
  2417.           && whichMess == NEWoNLY && canRespond()
  2418.           && (strCmpU(msgBuf.mbauth, logBuf.lbname) != SAMESTRING
  2419.               || msgBuf.mborig[0] != 0)  /* i.e. is not local mail */
  2420.           &&  strCmpU(msgBuf.mbauth, "Citadel") != SAMESTRING
  2421.           &&  msgBuf.mbauth[0] != 0   /* not anonymous mail> */
  2422.              )
  2423.             {
  2424.             if ((result = DoRespond()) != ERROR)
  2425.               {
  2426.               roomBuf.msg[i].rbmsgNo &= S_MSG_MASK;
  2427.               logBuf.lbMail[i].rbmsgNo &= S_MSG_MASK;
  2428.               if (result == TRUE)
  2429.                 {
  2430.                 if (replyMessage(msgNo,roomBuf.msg[i].rbmsgLoc))
  2431.                 i--;
  2432.                 if (whichIO != CONSOLE && thisRoom == MAILROOM)
  2433.                 echo = CALLER;   /* Restore privacy. */
  2434.                 outFlag = OUTOK;
  2435.  
  2436.                 }
  2437.  
  2438.               }
  2439.             else
  2440.               {
  2441.               roomBuf.msg[i].rbmsgNo |= (~S_MSG_MASK);
  2442.               logBuf.lbMail[i].rbmsgNo |= (~S_MSG_MASK);
  2443.  
  2444.               }
  2445.  
  2446.             }
  2447.  
  2448.           }
  2449.  
  2450.         }
  2451.  
  2452.       }
  2453.     i += increment;
  2454.  
  2455.     }
  2456.   echo = BOTH;
  2457.   Showing = WHATEVER;
  2458.   if (heldMess && PEUsed)
  2459.     {
  2460.     givePrompt();
  2461.     mPrintf("Current Held Message\n ");
  2462.     hldMessage(FALSE);
  2463.  
  2464.     }
  2465.   return MsgCount;
  2466.  
  2467.   }
  2468. /*
  2469. * InterruptMessage()
  2470. *
  2471. * This handles Pause-Enter.
  2472. */
  2473. char InterruptMessage()
  2474.   {
  2475.   char toReturn = FALSE;
  2476.   Showing = WHATEVER;
  2477.   if (heldMess)
  2478.     {
  2479.     if (hldMessage(FALSE)) toReturn = TRUE;
  2480.  
  2481.     }
  2482.   else
  2483.     {
  2484.     if (makeMessage(ASCII)) toReturn = TRUE;
  2485.  
  2486.     }
  2487.   MsgStreamEnter = FALSE;
  2488.   Showing = MSGS;
  2489.   outFlag = OUTOK;      /* so we can Pause later */
  2490.   return toReturn;
  2491.  
  2492.   }
  2493. /*
  2494. * ShowReply()
  2495. *
  2496. * This is the backlink tracer of a mail message.
  2497. */
  2498. void ShowReply(int i)   /* i is index into roomBuf.msgs */
  2499.   {
  2500.   char *ptr, doit = FALSE;
  2501.   MSG_NUMBER msg;
  2502.   label author, rec;
  2503.  
  2504.   /* format of mbreply is loc:msgNo */
  2505.   MsgStreamEnter = FALSE;
  2506.   outFlag = OUTOK;
  2507.   /* make sure there's a return ptr */
  2508.   if (strLen(msgBuf.mbreply) != 0 &&
  2509.   (ptr = strchr(msgBuf.mbreply, ':')) != NULL)
  2510.     {
  2511.     msg = atol(ptr + 1);
  2512.     doit = findMessage(atoi(msgBuf.mbreply), msg, TRUE);
  2513.     for (; i >= 0; i--)
  2514.     if (msg == (roomBuf.msg[i].rbmsgNo & S_MSG_MASK)) break;
  2515.  
  2516.     }
  2517.   else
  2518.     {
  2519.     /* else do a manual scan */
  2520.     strncpy(author, msgBuf.mbauth, sizeof(author)-1);
  2521.     strCpy(rec, msgBuf.mbto);
  2522.     for (--i; i >= 0; i--)
  2523.       {
  2524.       doit = findMessage(roomBuf.msg[i].rbmsgLoc,
  2525.       roomBuf.msg[i].rbmsgNo & S_MSG_MASK, TRUE);
  2526.       if (!doit) i = 0;
  2527.       else if (strCmpU(author, msgBuf.mbto) == SAMESTRING &&
  2528.       strCmpU(rec, msgBuf.mbauth) == SAMESTRING) break;
  2529.  
  2530.       }
  2531.  
  2532.     }
  2533.   /* this will allow streaming along on the message chain */
  2534.   if (doit)
  2535.     {
  2536.     printMessage(0);
  2537.     if (MsgStreamEnter)
  2538.     ShowReply(i);       /* fix this argument someday */
  2539.  
  2540.     }
  2541.  
  2542.   }
  2543. /*
  2544. * SetShowLimits()
  2545. *
  2546. * Sets up the limits for showing messages.
  2547. */
  2548. void SetShowLimits(char rev, int *start, int *finish, int *increment)
  2549.   {
  2550.   /* Allow for reverse retrieval: */
  2551.   if (!rev)
  2552.     {
  2553.     *start      = 0;
  2554.     *finish     = (thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM;
  2555.     *increment   = 1;
  2556.  
  2557.     }
  2558.   else
  2559.     {
  2560.     *start      = (((thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM) -1);
  2561.     *finish     = -1;
  2562.     *increment   = -1;
  2563.  
  2564.     }
  2565.  
  2566.   }
  2567. /*
  2568. * redirect()
  2569. *
  2570. * This function causes output to be redirected to a file.
  2571. */
  2572. char redirect(char *name)
  2573.   {
  2574.   extern char *APPEND_TEXT;
  2575.   char fullFileName[100];
  2576.   if (name != NULL) strCpy(fullFileName, name);
  2577.   else fullFileName[0] = 0;
  2578.   if (strLen(fullFileName) != 0 || getXString("EFILEN", fullFileName, 100,
  2579.   (strLen(jrnlFile) == 0) ? NULL : jrnlFile, jrnlFile))
  2580.     {
  2581.     if ((upfd = safeopen(fullFileName, APPEND_TEXT)) == NULL)
  2582.       {
  2583.       if (inNet == NON_NET)
  2584.       mPrintf("ERROR: Couldn't open output file %s\n ", fullFileName);
  2585.  
  2586.       }
  2587.     else
  2588.       {
  2589.       outPut = DISK;
  2590.       if (name == NULL) strCpy(jrnlFile, fullFileName);
  2591.       return TRUE;
  2592.  
  2593.       }
  2594.  
  2595.     }
  2596.   return FALSE;
  2597.  
  2598.   }
  2599. /*
  2600. * undirect()
  2601. *
  2602. * This makes output go back to normal.
  2603. */
  2604. void undirect()
  2605.   {
  2606.   fclose(upfd);
  2607.   outPut = NORMAL;
  2608.  
  2609.   }
  2610. #define UnknownRoute "\n Couldn't identify route (%s).", msgBuf.mbaddr
  2611. /*
  2612. * TellRoute()
  2613. *
  2614. * This will figure out where this message came from.
  2615. */
  2616. void TellRoute()
  2617.   {
  2618.   extern char *LOC_NET, *NON_LOC_NET;
  2619.   int slot;
  2620.   if (strCmp(msgBuf.mboname, cfg.codeBuf + cfg.nodeName) != SAMESTRING &&
  2621.   strLen(msgBuf.mbaddr) != 0)
  2622.     {
  2623.     if (((slot = RoutePath(NON_LOC_NET, msgBuf.mbaddr)) != ERROR ||
  2624.     (slot = RoutePath(LOC_NET, msgBuf.mbaddr)) != ERROR) &&
  2625.     slot >= 0 && slot < cfg.netSize)
  2626.       {
  2627.       if (slot != thisNet)
  2628.       getNet(slot, &netBuf);
  2629.       if (netBuf.nbflags.in_use)
  2630.       mPrintf("\n Routed from %s.", netBuf.netName);
  2631.       else
  2632.       mPrintf(UnknownRoute);
  2633.  
  2634.       }
  2635.     else
  2636.     mPrintf(UnknownRoute);
  2637.  
  2638.     }
  2639.  
  2640.   }
  2641. /*
  2642. * FindNextFile()
  2643. *
  2644. * This finds next route filename in sequence.
  2645. */
  2646. int FindNextFile(char *base)
  2647.   {
  2648.   int rover = 0;
  2649.   char *fn;
  2650.   if (cfg.BoolFlags.debug) splitF(netLog," FindNextFile(%s)",base);
  2651.   fn = GetDynamic(strLen(base) + 5);
  2652.   /* Find next unused file name */
  2653.   do
  2654.     {
  2655.     sPrintf(fn, "%s.%d", base, rover++);
  2656.  
  2657.     }
  2658.   while (access(fn, 0) != -1);
  2659.   strCpy(base, fn);
  2660.   free(fn);
  2661.   rover--;
  2662.   if (cfg.BoolFlags.debug) splitF(netLog,"=%d\n",rover);
  2663.   return rover;
  2664.  
  2665.   }
  2666. /*
  2667. * TranslateFilename()
  2668. *
  2669. * This does translations on a filename.  This is used for embedding dates
  2670. * or numbers into a filename.
  2671. */
  2672. void TranslateFilename(char *realfn, char *fn)
  2673.   {
  2674.   int year, day, hours, minutes;
  2675.   char *month;
  2676.   getCdate(&year, &month, &day, &hours, &minutes);
  2677.   do
  2678.     {
  2679.     *realfn = *fn;
  2680.     if (*fn == '%')
  2681.       {
  2682.       fn++;
  2683.       switch (*fn)
  2684.         {
  2685.         case 'm':
  2686.         case 'M':
  2687.         sPrintf(realfn, "%s", month);
  2688.         break;
  2689.         case 'y':
  2690.         case 'Y':
  2691.         sPrintf(realfn, "%d", year);
  2692.         break;
  2693.         default:
  2694.         sPrintf(realfn, "%c", *fn);
  2695.         break;
  2696.  
  2697.         }
  2698.       while (*(realfn + 1))
  2699.       realfn++;
  2700.  
  2701.       }
  2702.     realfn++;
  2703.  
  2704.     }
  2705.   while (*fn++);
  2706.  
  2707.   }
  2708.